dev
dart 123 lines 3.75 KB
Raw
1 import 'dart:async';
2 import 'dart:ffi';
3 import 'dart:typed_data';
4
5 import 'package:cw_core/hardware/hardware_wallet_service.dart';
6 import 'package:cw_core/utils/print_verbose.dart';
7 import 'package:ffi/ffi.dart';
8 import 'package:flutter/foundation.dart';
9 import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
10 import 'package:ledger_flutter_plus/ledger_flutter_plus_dart.dart';
11 import 'package:monero/src/monero.dart' as api;
12
13 LedgerConnection? gLedger;
14 String? latestLedgerCommand;
15
16 typedef LedgerCallback = Void Function(Pointer<UnsignedChar>, UnsignedInt);
17 NativeCallable<LedgerCallback>? callable;
18
19 Future<void> enableLedgerExchange(LedgerConnection connection) async {
20 callable?.close();
21
22 void callback(Pointer<UnsignedChar> request, int requestLength) async {
23 final ledgerRequest = request.cast<Uint8>().asTypedList(requestLength);
24
25 _logLedgerCommand(ledgerRequest, false);
26 final response = await exchange(connection, ledgerRequest);
27 _logLedgerCommand(response, true);
28
29 final Pointer<Uint8> result = malloc<Uint8>(response.length);
30 for (var i = 0; i < response.length; i++) {
31 result.asTypedList(response.length)[i] = response[i];
32 }
33
34 latestLedgerCommand = _ledgerMoneroCommands[ledgerRequest[1]];
35
36 api.MoneroWallet.setDeviceReceivedData(result.cast<UnsignedChar>(), response.length);
37 malloc.free(result);
38 // api.MoneroFree().free(result.cast());
39 }
40
41 callable = NativeCallable<LedgerCallback>.listener(callback);
42 api.MoneroWallet.setLedgerCallback(callable!.nativeFunction);
43 }
44
45 void disableLedgerExchange() {
46 callable?.close();
47 gLedger?.disconnect();
48 gLedger = null;
49 latestLedgerCommand = null;
50 }
51
52 Future<Uint8List> exchange(LedgerConnection connection, Uint8List data) async =>
53 connection.sendOperation<Uint8List>(ExchangeOperation(data));
54
55 class ExchangeOperation extends LedgerRawOperation<Uint8List> {
56 final Uint8List inputData;
57
58 ExchangeOperation(this.inputData);
59
60 @override
61 Future<Uint8List> read(ByteDataReader reader) async => reader.read(reader.remainingLength);
62
63 @override
64 Future<List<Uint8List>> write(ByteDataWriter writer) async => [inputData];
65 }
66
67 const _ledgerMoneroCommands = {
68 0x00: "INS_NONE",
69 0x02: "INS_RESET",
70 0x20: "INS_GET_KEY",
71 0x21: "INS_DISPLAY_ADDRESS",
72 0x22: "INS_PUT_KEY",
73 0x24: "INS_GET_CHACHA8_PREKEY",
74 0x26: "INS_VERIFY_KEY",
75 0x28: "INS_MANAGE_SEEDWORDS",
76 0x30: "INS_SECRET_KEY_TO_PUBLIC_KEY",
77 0x32: "INS_GEN_KEY_DERIVATION",
78 0x34: "INS_DERIVATION_TO_SCALAR",
79 0x36: "INS_DERIVE_PUBLIC_KEY",
80 0x38: "INS_DERIVE_SECRET_KEY",
81 0x3A: "INS_GEN_KEY_IMAGE",
82 0x3B: "INS_DERIVE_VIEW_TAG",
83 0x3C: "INS_SECRET_KEY_ADD",
84 0x3E: "INS_SECRET_KEY_SUB",
85 0x40: "INS_GENERATE_KEYPAIR",
86 0x42: "INS_SECRET_SCAL_MUL_KEY",
87 0x44: "INS_SECRET_SCAL_MUL_BASE",
88 0x46: "INS_DERIVE_SUBADDRESS_PUBLIC_KEY",
89 0x48: "INS_GET_SUBADDRESS",
90 0x4A: "INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY",
91 0x4C: "INS_GET_SUBADDRESS_SECRET_KEY",
92 0x70: "INS_OPEN_TX",
93 0x72: "INS_SET_SIGNATURE_MODE",
94 0x74: "INS_GET_ADDITIONAL_KEY",
95 0x76: "INS_STEALTH",
96 0x77: "INS_GEN_COMMITMENT_MASK",
97 0x78: "INS_BLIND",
98 0x7A: "INS_UNBLIND",
99 0x7B: "INS_GEN_TXOUT_KEYS",
100 0x7D: "INS_PREFIX_HASH",
101 0x7C: "INS_VALIDATE",
102 0x7E: "INS_MLSAG",
103 0x7F: "INS_CLSAG",
104 0x80: "INS_CLOSE_TX",
105 0xA0: "INS_GET_TX_PROOF",
106 0xC0: "INS_GET_RESPONSE"
107 };
108
109 void _logLedgerCommand(Uint8List command, [bool isResponse = true]) {
110 String toHexString(Uint8List data) => data.map((e) => e.toRadixString(16).padLeft(2, '0')).join();
111
112 if (isResponse) {
113 printV("< ${toHexString(command)}");
114 } else {
115 printV("> ${_ledgerMoneroCommands[command[1]]} ${toHexString(command.sublist(2))}");
116 }
117 }
118
119 class MoneroLedgerService extends HardwareWalletService {
120 final LedgerConnection connection;
121
122 MoneroLedgerService(this.connection);
123 }