@ledgerhq/hw-app-canton 0.3.0 → 0.3.1-nightly.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/MockDevice.ts DELETED
@@ -1,170 +0,0 @@
1
- import { CantonAddress, CantonSignature } from "@ledgerhq/coin-canton";
2
-
3
- const INS = {
4
- GET_VERSION: 0x04,
5
- GET_ADDR: 0x05,
6
- SIGN: 0x06,
7
- };
8
-
9
- const STATUS = {
10
- OK: 0x9000,
11
- USER_CANCEL: 0x6985,
12
- };
13
-
14
- export type AppConfig = {
15
- version: string;
16
- };
17
-
18
- // SECP256R1-compatible mock addresses
19
- const SECP256R1_MOCK_ADDRESSES = {
20
- "44'/6767'/0'/0'/0'": {
21
- // Uncompressed SECP256R1 public key (65 bytes: 0x04 + 32-byte X + 32-byte Y)
22
- publicKey:
23
- "0x04" +
24
- "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" +
25
- "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
26
- address: "canton_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z",
27
- },
28
- "44'/6767'/0'/0'/1'": {
29
- // Another valid SECP256R1 public key
30
- publicKey:
31
- "0x04" +
32
- "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890" +
33
- "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd",
34
- address: "canton_2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7a",
35
- },
36
- };
37
-
38
- /**
39
- * Mock Canton "@ledgerhq/hw-app-canton" device implementation for development and testing
40
- */
41
- export class MockCantonDevice {
42
- private mockAddresses: Map<string, CantonAddress> = new Map();
43
- private mockSignatures: Map<string, CantonSignature> = new Map();
44
-
45
- constructor() {
46
- // Initialize with SECP256R1-compatible addresses
47
- Object.entries(SECP256R1_MOCK_ADDRESSES).forEach(([path, address]) => {
48
- this.mockAddresses.set(path, address);
49
- });
50
- }
51
-
52
- async send(cla: number, ins: number, p1: number, p2: number, data: Buffer): Promise<Buffer> {
53
- switch (ins) {
54
- case INS.GET_ADDR:
55
- return this.getAddressResponse(data);
56
- case INS.SIGN:
57
- return this.signTransactionResponse(data);
58
- case INS.GET_VERSION:
59
- return this.getAppConfigurationResponse();
60
- default:
61
- throw new Error(`Unsupported instruction: ${ins}`);
62
- }
63
- }
64
-
65
- private async getAddressResponse(data: Buffer): Promise<Buffer> {
66
- await this.simulateDeviceDelay();
67
-
68
- // Parse the path from the data
69
- const pathData = data.slice(1); // Skip length byte
70
- const path = this.parsePathFromData(pathData);
71
-
72
- const mockAddress = this.mockAddresses.get(path);
73
- if (!mockAddress) {
74
- // Generate SECP256R1-compatible mock address
75
- const pathHash = this.hashString(path);
76
- const newAddress: CantonAddress = {
77
- // Create valid uncompressed SECP256R1 public key format
78
- publicKey:
79
- "0x04" +
80
- pathHash.substring(0, 64).padEnd(64, "0") +
81
- pathHash.substring(64, 128).padEnd(64, "0"),
82
- address: `canton_${pathHash.substring(0, 36)}`,
83
- };
84
- this.mockAddresses.set(path, newAddress);
85
- }
86
-
87
- const address = this.mockAddresses.get(path)!;
88
-
89
- // Return 65-byte uncompressed public key (0x04 + 32-byte X + 32-byte Y)
90
- const publicKeyBytes = Buffer.from(address.publicKey.slice(2), "hex");
91
- const response = Buffer.alloc(65 + 2);
92
- publicKeyBytes.copy(response, 0);
93
- response.writeUInt16BE(STATUS.OK, 65);
94
-
95
- return response;
96
- }
97
-
98
- private async signTransactionResponse(data: Buffer): Promise<Buffer> {
99
- await this.simulateDeviceDelay();
100
-
101
- // Parse the path and transaction from the data
102
- const pathData = data.slice(0, 21); // First 21 bytes are path
103
- const txData = data.slice(21); // Rest is transaction data
104
- const path = this.parsePathFromData(pathData);
105
-
106
- const signatureKey = `${path}:${txData.toString("hex")}`;
107
- let signature = this.mockSignatures.get(signatureKey);
108
-
109
- if (!signature) {
110
- // Generate SECP256R1-compatible mock signature (64 bytes: r + s)
111
- const combinedHash = this.hashString(signatureKey);
112
- signature = `0x${combinedHash.substring(0, 64).padEnd(64, "0")}`;
113
- this.mockSignatures.set(signatureKey, signature);
114
- }
115
-
116
- // Return 64-byte signature (r + s components)
117
- const response = Buffer.alloc(64 + 2);
118
- Buffer.from(signature.slice(2), "hex").copy(response, 0); // Remove '0x' prefix
119
- response.writeUInt16BE(STATUS.OK, 64);
120
-
121
- return response;
122
- }
123
-
124
- private async getAppConfigurationResponse(): Promise<Buffer> {
125
- await this.simulateDeviceDelay();
126
-
127
- // Create response buffer: version data + status code
128
- const versionData = Buffer.from([0x00, 0x01, 0x00, 0x06]); // Version 0.1.0
129
- const response = Buffer.alloc(versionData.length + 2);
130
- versionData.copy(response, 0);
131
- response.writeUInt16BE(STATUS.OK, versionData.length);
132
-
133
- return response;
134
- }
135
-
136
- private parsePathFromData(data: Buffer): string {
137
- // Convert the path data back to a BIP32 path string
138
- const segments: number[] = [];
139
- for (let i = 0; i < data.length; i += 4) {
140
- const segment = data.readUInt32BE(i);
141
- segments.push(segment);
142
- }
143
-
144
- // Convert to BIP32 path string
145
- const pathParts = segments.map(seg => {
146
- const isHardened = (seg & 0x80000000) !== 0;
147
- const value = seg & 0x7fffffff;
148
- return isHardened ? `${value}'` : `${value}`;
149
- });
150
-
151
- return pathParts.join("/");
152
- }
153
-
154
- private async simulateDeviceDelay(): Promise<void> {
155
- await new Promise(resolve => setTimeout(resolve, 50));
156
- }
157
-
158
- /**
159
- * Simple deterministic hash function for generating mock data
160
- */
161
- private hashString(str: string): string {
162
- let hash = 0;
163
- for (let i = 0; i < str.length; i++) {
164
- const char = str.charCodeAt(i);
165
- hash = (hash << 5) - hash + char;
166
- hash = hash & hash; // Convert to 32-bit integer
167
- }
168
- return Math.abs(hash).toString(16);
169
- }
170
- }