@bitshard.io/bitshard-sdk 0.0.1
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/dist/BitShardSDK.d.ts +156 -0
- package/dist/BitShardSDK.d.ts.map +1 -0
- package/dist/BitShardSDK.js +350 -0
- package/dist/BitShardSDK.js.map +1 -0
- package/dist/chains/bitcoin/BitcoinChain.d.ts +6 -0
- package/dist/chains/bitcoin/BitcoinChain.d.ts.map +1 -0
- package/dist/chains/bitcoin/BitcoinChain.js +10 -0
- package/dist/chains/bitcoin/BitcoinChain.js.map +1 -0
- package/dist/chains/config.d.ts +5 -0
- package/dist/chains/config.d.ts.map +1 -0
- package/dist/chains/config.js +7 -0
- package/dist/chains/config.js.map +1 -0
- package/dist/chains/evm/EVMChain.d.ts +6 -0
- package/dist/chains/evm/EVMChain.d.ts.map +1 -0
- package/dist/chains/evm/EVMChain.js +10 -0
- package/dist/chains/evm/EVMChain.js.map +1 -0
- package/dist/core/DKLSParty.d.ts +132 -0
- package/dist/core/DKLSParty.d.ts.map +1 -0
- package/dist/core/DKLSParty.js +267 -0
- package/dist/core/DKLSParty.js.map +1 -0
- package/dist/core/DKLSService.d.ts +83 -0
- package/dist/core/DKLSService.d.ts.map +1 -0
- package/dist/core/DKLSService.js +325 -0
- package/dist/core/DKLSService.js.map +1 -0
- package/dist/core/ThresholdConfig.d.ts +76 -0
- package/dist/core/ThresholdConfig.d.ts.map +1 -0
- package/dist/core/ThresholdConfig.js +127 -0
- package/dist/core/ThresholdConfig.js.map +1 -0
- package/dist/core/types.d.ts +238 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/crypto/addresses.d.ts +82 -0
- package/dist/crypto/addresses.d.ts.map +1 -0
- package/dist/crypto/addresses.js +242 -0
- package/dist/crypto/addresses.js.map +1 -0
- package/dist/crypto/elliptic.d.ts +19 -0
- package/dist/crypto/elliptic.d.ts.map +1 -0
- package/dist/crypto/elliptic.js +114 -0
- package/dist/crypto/elliptic.js.map +1 -0
- package/dist/crypto/encoding.d.ts +111 -0
- package/dist/crypto/encoding.d.ts.map +1 -0
- package/dist/crypto/encoding.js +224 -0
- package/dist/crypto/encoding.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/protocols/keygen.d.ts +6 -0
- package/dist/protocols/keygen.d.ts.map +1 -0
- package/dist/protocols/keygen.js +12 -0
- package/dist/protocols/keygen.js.map +1 -0
- package/dist/protocols/presignature.d.ts +68 -0
- package/dist/protocols/presignature.d.ts.map +1 -0
- package/dist/protocols/presignature.js +147 -0
- package/dist/protocols/presignature.js.map +1 -0
- package/dist/protocols/refresh.d.ts +5 -0
- package/dist/protocols/refresh.d.ts.map +1 -0
- package/dist/protocols/refresh.js +10 -0
- package/dist/protocols/refresh.js.map +1 -0
- package/dist/protocols/signing.d.ts +7 -0
- package/dist/protocols/signing.d.ts.map +1 -0
- package/dist/protocols/signing.js +12 -0
- package/dist/protocols/signing.js.map +1 -0
- package/dist/rpc/RPCProvider.d.ts +6 -0
- package/dist/rpc/RPCProvider.d.ts.map +1 -0
- package/dist/rpc/RPCProvider.js +6 -0
- package/dist/rpc/RPCProvider.js.map +1 -0
- package/dist/rpc/methods.d.ts +5 -0
- package/dist/rpc/methods.d.ts.map +1 -0
- package/dist/rpc/methods.js +10 -0
- package/dist/rpc/methods.js.map +1 -0
- package/dist/websocket/coordinator.d.ts +6 -0
- package/dist/websocket/coordinator.d.ts.map +1 -0
- package/dist/websocket/coordinator.js +10 -0
- package/dist/websocket/coordinator.js.map +1 -0
- package/dist/websocket/messages.d.ts +9 -0
- package/dist/websocket/messages.d.ts.map +1 -0
- package/dist/websocket/messages.js +7 -0
- package/dist/websocket/messages.js.map +1 -0
- package/dist/websocket/session.d.ts +5 -0
- package/dist/websocket/session.d.ts.map +1 -0
- package/dist/websocket/session.js +7 -0
- package/dist/websocket/session.js.map +1 -0
- package/dist/wire/format.d.ts +8 -0
- package/dist/wire/format.d.ts.map +1 -0
- package/dist/wire/format.js +11 -0
- package/dist/wire/format.js.map +1 -0
- package/dist/wire/validation.d.ts +6 -0
- package/dist/wire/validation.d.ts.map +1 -0
- package/dist/wire/validation.js +13 -0
- package/dist/wire/validation.js.map +1 -0
- package/package.json +67 -0
- package/src/BitShardSDK.ts +428 -0
- package/src/chains/bitcoin/BitcoinChain.ts +7 -0
- package/src/chains/config.ts +7 -0
- package/src/chains/evm/EVMChain.ts +7 -0
- package/src/core/DKLSParty.ts +317 -0
- package/src/core/DKLSService.ts +426 -0
- package/src/core/ThresholdConfig.ts +159 -0
- package/src/core/types.ts +253 -0
- package/src/crypto/addresses.ts +282 -0
- package/src/crypto/elliptic.ts +133 -0
- package/src/crypto/encoding.ts +227 -0
- package/src/index.ts +40 -0
- package/src/protocols/keygen.ts +8 -0
- package/src/protocols/presignature.ts +196 -0
- package/src/protocols/refresh.ts +7 -0
- package/src/protocols/signing.ts +9 -0
- package/src/rpc/RPCProvider.ts +7 -0
- package/src/rpc/methods.ts +7 -0
- package/src/websocket/coordinator.ts +7 -0
- package/src/websocket/messages.ts +11 -0
- package/src/websocket/session.ts +7 -0
- package/src/wire/format.ts +10 -0
- package/src/wire/validation.ts +14 -0
- package/test-sdk.js +234 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encoding utilities for cryptocurrency operations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert hex string to Uint8Array
|
|
7
|
+
* @param hex - Hex string (with or without 0x prefix)
|
|
8
|
+
* @returns Uint8Array
|
|
9
|
+
*/
|
|
10
|
+
export function hexToBytes(hex: string): Uint8Array {
|
|
11
|
+
const cleanHex = hex.replace(/^0x/, '');
|
|
12
|
+
if (cleanHex.length % 2 !== 0) {
|
|
13
|
+
throw new Error('Hex string must have even length');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
17
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
18
|
+
bytes[i] = parseInt(cleanHex.substr(i * 2, 2), 16);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return bytes;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Convert Uint8Array to hex string
|
|
26
|
+
* @param bytes - Uint8Array
|
|
27
|
+
* @param prefix - Whether to add 0x prefix (default: false)
|
|
28
|
+
* @returns Hex string
|
|
29
|
+
*/
|
|
30
|
+
export function bytesToHex(bytes: Uint8Array, prefix: boolean = false): string {
|
|
31
|
+
const hex = Array.from(bytes)
|
|
32
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
33
|
+
.join('');
|
|
34
|
+
|
|
35
|
+
return prefix ? '0x' + hex : hex;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convert Buffer to hex string
|
|
40
|
+
* @param buffer - Buffer
|
|
41
|
+
* @param prefix - Whether to add 0x prefix (default: false)
|
|
42
|
+
* @returns Hex string
|
|
43
|
+
*/
|
|
44
|
+
export function bufferToHex(buffer: Buffer, prefix: boolean = false): string {
|
|
45
|
+
const hex = buffer.toString('hex');
|
|
46
|
+
return prefix ? '0x' + hex : hex;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Convert hex string to Buffer
|
|
51
|
+
* @param hex - Hex string (with or without 0x prefix)
|
|
52
|
+
* @returns Buffer
|
|
53
|
+
*/
|
|
54
|
+
export function hexToBuffer(hex: string): Buffer {
|
|
55
|
+
const cleanHex = hex.replace(/^0x/, '');
|
|
56
|
+
return Buffer.from(cleanHex, 'hex');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Convert base64 string to Uint8Array
|
|
61
|
+
* @param base64 - Base64 encoded string
|
|
62
|
+
* @returns Uint8Array
|
|
63
|
+
*/
|
|
64
|
+
export function base64ToBytes(base64: string): Uint8Array {
|
|
65
|
+
return new Uint8Array(Buffer.from(base64, 'base64'));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Convert Uint8Array to base64 string
|
|
70
|
+
* @param bytes - Uint8Array
|
|
71
|
+
* @returns Base64 encoded string
|
|
72
|
+
*/
|
|
73
|
+
export function bytesToBase64(bytes: Uint8Array): string {
|
|
74
|
+
return Buffer.from(bytes).toString('base64');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Convert UTF-8 string to Uint8Array
|
|
79
|
+
* @param str - UTF-8 string
|
|
80
|
+
* @returns Uint8Array
|
|
81
|
+
*/
|
|
82
|
+
export function utf8ToBytes(str: string): Uint8Array {
|
|
83
|
+
return new TextEncoder().encode(str);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Convert Uint8Array to UTF-8 string
|
|
88
|
+
* @param bytes - Uint8Array
|
|
89
|
+
* @returns UTF-8 string
|
|
90
|
+
*/
|
|
91
|
+
export function bytesToUtf8(bytes: Uint8Array): string {
|
|
92
|
+
return new TextDecoder().decode(bytes);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Concatenate multiple Uint8Arrays
|
|
97
|
+
* @param arrays - Arrays to concatenate
|
|
98
|
+
* @returns Combined Uint8Array
|
|
99
|
+
*/
|
|
100
|
+
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
|
101
|
+
const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
|
|
102
|
+
const result = new Uint8Array(totalLength);
|
|
103
|
+
|
|
104
|
+
let offset = 0;
|
|
105
|
+
for (const arr of arrays) {
|
|
106
|
+
result.set(arr, offset);
|
|
107
|
+
offset += arr.length;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Compare two Uint8Arrays for equality
|
|
115
|
+
* @param a - First array
|
|
116
|
+
* @param b - Second array
|
|
117
|
+
* @returns True if arrays are equal
|
|
118
|
+
*/
|
|
119
|
+
export function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
|
|
120
|
+
if (a.length !== b.length) return false;
|
|
121
|
+
|
|
122
|
+
for (let i = 0; i < a.length; i++) {
|
|
123
|
+
if (a[i] !== b[i]) return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Convert number to big-endian Uint8Array
|
|
131
|
+
* @param num - Number to convert
|
|
132
|
+
* @param bytes - Number of bytes (default: 4)
|
|
133
|
+
* @returns Uint8Array in big-endian format
|
|
134
|
+
*/
|
|
135
|
+
export function numberToBytes(num: number, bytes: number = 4): Uint8Array {
|
|
136
|
+
const arr = new Uint8Array(bytes);
|
|
137
|
+
for (let i = bytes - 1; i >= 0; i--) {
|
|
138
|
+
arr[i] = num & 0xff;
|
|
139
|
+
num = num >> 8;
|
|
140
|
+
}
|
|
141
|
+
return arr;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Convert big-endian Uint8Array to number
|
|
146
|
+
* @param bytes - Uint8Array in big-endian format
|
|
147
|
+
* @returns Number
|
|
148
|
+
*/
|
|
149
|
+
export function bytesToNumber(bytes: Uint8Array): number {
|
|
150
|
+
let num = 0;
|
|
151
|
+
for (const byte of bytes) {
|
|
152
|
+
num = (num << 8) | byte;
|
|
153
|
+
}
|
|
154
|
+
return num;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Convert BigInt to Uint8Array
|
|
159
|
+
* @param bigint - BigInt value
|
|
160
|
+
* @param length - Optional fixed length (pads with zeros)
|
|
161
|
+
* @returns Uint8Array
|
|
162
|
+
*/
|
|
163
|
+
export function bigintToBytes(bigint: bigint, length?: number): Uint8Array {
|
|
164
|
+
let hex = bigint.toString(16);
|
|
165
|
+
if (hex.length % 2 !== 0) {
|
|
166
|
+
hex = '0' + hex;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const bytes = hexToBytes(hex);
|
|
170
|
+
|
|
171
|
+
if (length && bytes.length < length) {
|
|
172
|
+
const padded = new Uint8Array(length);
|
|
173
|
+
padded.set(bytes, length - bytes.length);
|
|
174
|
+
return padded;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return bytes;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Convert Uint8Array to BigInt
|
|
182
|
+
* @param bytes - Uint8Array
|
|
183
|
+
* @returns BigInt
|
|
184
|
+
*/
|
|
185
|
+
export function bytesToBigint(bytes: Uint8Array): bigint {
|
|
186
|
+
if (bytes.length === 0) return 0n;
|
|
187
|
+
return BigInt('0x' + bytesToHex(bytes));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Generate random bytes
|
|
192
|
+
* @param length - Number of bytes to generate
|
|
193
|
+
* @returns Random Uint8Array
|
|
194
|
+
*/
|
|
195
|
+
export function randomBytes(length: number): Uint8Array {
|
|
196
|
+
const bytes = new Uint8Array(length);
|
|
197
|
+
if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.getRandomValues) {
|
|
198
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
199
|
+
} else {
|
|
200
|
+
// Fallback for Node.js
|
|
201
|
+
const crypto = require('crypto');
|
|
202
|
+
const buffer = crypto.randomBytes(length);
|
|
203
|
+
bytes.set(buffer);
|
|
204
|
+
}
|
|
205
|
+
return bytes;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Hash data using SHA256
|
|
210
|
+
* @param data - Data to hash
|
|
211
|
+
* @returns SHA256 hash as Uint8Array
|
|
212
|
+
*/
|
|
213
|
+
export function sha256(data: Uint8Array): Uint8Array {
|
|
214
|
+
const crypto = require('crypto');
|
|
215
|
+
const hash = crypto.createHash('sha256');
|
|
216
|
+
hash.update(data);
|
|
217
|
+
return new Uint8Array(hash.digest());
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Double SHA256 hash (used in Bitcoin)
|
|
222
|
+
* @param data - Data to hash
|
|
223
|
+
* @returns Double SHA256 hash as Uint8Array
|
|
224
|
+
*/
|
|
225
|
+
export function doubleSha256(data: Uint8Array): Uint8Array {
|
|
226
|
+
return sha256(sha256(data));
|
|
227
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Core exports
|
|
2
|
+
export { DKLSService } from './core/DKLSService';
|
|
3
|
+
export { DKLSParty } from './core/DKLSParty';
|
|
4
|
+
export { ThresholdConfig } from './core/ThresholdConfig';
|
|
5
|
+
export * from './core/types';
|
|
6
|
+
|
|
7
|
+
// Crypto exports
|
|
8
|
+
export * from './crypto/addresses';
|
|
9
|
+
export * from './crypto/encoding';
|
|
10
|
+
|
|
11
|
+
// Protocol exports
|
|
12
|
+
export * from './protocols/keygen';
|
|
13
|
+
export * from './protocols/signing';
|
|
14
|
+
export * from './protocols/presignature';
|
|
15
|
+
export * from './protocols/refresh';
|
|
16
|
+
|
|
17
|
+
// Chain exports
|
|
18
|
+
export * from './chains/config';
|
|
19
|
+
export * from './chains/evm/EVMChain';
|
|
20
|
+
export * from './chains/bitcoin/BitcoinChain';
|
|
21
|
+
|
|
22
|
+
// RPC exports
|
|
23
|
+
export * from './rpc/RPCProvider';
|
|
24
|
+
export * from './rpc/methods';
|
|
25
|
+
|
|
26
|
+
// Wire format exports
|
|
27
|
+
export * from './wire/format';
|
|
28
|
+
export * from './wire/validation';
|
|
29
|
+
|
|
30
|
+
// WebSocket exports
|
|
31
|
+
export * from './websocket/coordinator';
|
|
32
|
+
export * from './websocket/session';
|
|
33
|
+
export * from './websocket/messages';
|
|
34
|
+
|
|
35
|
+
// Main SDK class
|
|
36
|
+
export { BitShardSDK } from './BitShardSDK';
|
|
37
|
+
|
|
38
|
+
// Re-export classes and types from WASM library
|
|
39
|
+
// This ensures all consumers use the same WASM instance
|
|
40
|
+
export { Message, Keyshare, KeygenSession, SignSession } from '@silencelaboratories/dkls-wasm-ll-node';
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-signature generation and management
|
|
3
|
+
*
|
|
4
|
+
* CRITICAL SECURITY WARNING:
|
|
5
|
+
* Pre-signatures MUST NOT be reused. Reusing a pre-signature will expose the private key.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { SignSession, Keyshare, Message } from '@silencelaboratories/dkls-wasm-ll-node';
|
|
9
|
+
import type { PreSignature } from '../core/types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Pre-signature manager for secure pre-signature generation and tracking
|
|
13
|
+
*/
|
|
14
|
+
export class PreSignatureManager {
|
|
15
|
+
private consumedPreSignatures: Set<string> = new Set();
|
|
16
|
+
private preSignaturePool: Map<string, PreSignature> = new Map();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate pre-signatures that can be computed ahead of time
|
|
20
|
+
* @param keyshares Array of keyshares
|
|
21
|
+
* @param threshold Threshold value
|
|
22
|
+
* @param count Number of pre-signatures to generate
|
|
23
|
+
* @returns Array of pre-signatures
|
|
24
|
+
*/
|
|
25
|
+
async generatePreSignatures(
|
|
26
|
+
keyshares: Keyshare[],
|
|
27
|
+
threshold: number,
|
|
28
|
+
count: number = 1
|
|
29
|
+
): Promise<PreSignature[]> {
|
|
30
|
+
if (keyshares.length < threshold) {
|
|
31
|
+
throw new Error(`Insufficient keyshares: need ${threshold}, got ${keyshares.length}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const preSignatures: PreSignature[] = [];
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < count; i++) {
|
|
37
|
+
const id = this.generateId();
|
|
38
|
+
|
|
39
|
+
// Use first threshold keyshares
|
|
40
|
+
const signingShares = keyshares.slice(0, threshold);
|
|
41
|
+
|
|
42
|
+
// Create sign sessions
|
|
43
|
+
const parties: SignSession[] = signingShares.map(ks =>
|
|
44
|
+
new SignSession(ks, "m")
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Execute pre-signature rounds
|
|
48
|
+
const preSignature = await this.executePreSignatureRounds(
|
|
49
|
+
parties,
|
|
50
|
+
id
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
preSignatures.push(preSignature);
|
|
54
|
+
this.preSignaturePool.set(id, preSignature);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return preSignatures;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Execute pre-signature generation rounds
|
|
62
|
+
*/
|
|
63
|
+
private async executePreSignatureRounds(
|
|
64
|
+
parties: SignSession[],
|
|
65
|
+
id: string
|
|
66
|
+
): Promise<PreSignature> {
|
|
67
|
+
// Round 1: Create first messages
|
|
68
|
+
const msg1: Message[] = parties.map(p => p.createFirstMessage());
|
|
69
|
+
const selfIds: number[] = msg1.map(m => m.from_id);
|
|
70
|
+
|
|
71
|
+
// Round 2: Broadcast first messages
|
|
72
|
+
const msg2: Message[] = parties.flatMap((p, idx) =>
|
|
73
|
+
p.handleMessages(this.filterMessages(msg1, selfIds[idx]!))
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Round 3: Handle P2P messages
|
|
77
|
+
const msg3: Message[] = parties.flatMap((p, idx) =>
|
|
78
|
+
p.handleMessages(this.selectMessages(msg2, selfIds[idx]!))
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Round 4: Complete pre-signature
|
|
82
|
+
parties.forEach((p, idx) =>
|
|
83
|
+
p.handleMessages(this.selectMessages(msg3, selfIds[idx]!))
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
id,
|
|
88
|
+
parties,
|
|
89
|
+
createdAt: new Date(),
|
|
90
|
+
consumed: false
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Use a pre-signature to sign a message
|
|
96
|
+
* @param messageHash Message hash to sign
|
|
97
|
+
* @param preSignature Pre-signature to consume
|
|
98
|
+
* @returns Signature components
|
|
99
|
+
*/
|
|
100
|
+
async signWithPreSignature(
|
|
101
|
+
messageHash: Uint8Array,
|
|
102
|
+
preSignature: PreSignature
|
|
103
|
+
): Promise<[Uint8Array, Uint8Array]> {
|
|
104
|
+
// Check if pre-signature has already been used
|
|
105
|
+
if (this.consumedPreSignatures.has(preSignature.id)) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
'CRITICAL: Pre-signature has already been used! ' +
|
|
108
|
+
'Reusing pre-signatures exposes the private key!'
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (preSignature.consumed) {
|
|
113
|
+
throw new Error('Pre-signature has already been consumed');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Mark as consumed IMMEDIATELY
|
|
117
|
+
this.consumedPreSignatures.add(preSignature.id);
|
|
118
|
+
preSignature.consumed = true;
|
|
119
|
+
|
|
120
|
+
const { parties } = preSignature;
|
|
121
|
+
const selfIds = parties.map((_, idx) => idx);
|
|
122
|
+
|
|
123
|
+
// Generate last message with actual message hash
|
|
124
|
+
const msg4: Message[] = parties.map(p => p.lastMessage(messageHash));
|
|
125
|
+
|
|
126
|
+
// Combine to produce signature (consumes session)
|
|
127
|
+
const signatures = parties.map((p, idx) =>
|
|
128
|
+
p.combine(this.filterMessages(msg4, selfIds[idx]!))
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// All parties should produce the same signature
|
|
132
|
+
return signatures[0]!;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Check if a pre-signature has been consumed
|
|
137
|
+
* @param id Pre-signature ID
|
|
138
|
+
* @returns True if consumed
|
|
139
|
+
*/
|
|
140
|
+
isConsumed(id: string): boolean {
|
|
141
|
+
return this.consumedPreSignatures.has(id);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get available pre-signature count
|
|
146
|
+
* @returns Number of unused pre-signatures
|
|
147
|
+
*/
|
|
148
|
+
getAvailableCount(): number {
|
|
149
|
+
return Array.from(this.preSignaturePool.values())
|
|
150
|
+
.filter(ps => !ps.consumed).length;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get next available pre-signature
|
|
155
|
+
* @returns Pre-signature or undefined
|
|
156
|
+
*/
|
|
157
|
+
getNextAvailable(): PreSignature | undefined {
|
|
158
|
+
return Array.from(this.preSignaturePool.values())
|
|
159
|
+
.find(ps => !ps.consumed);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Clear consumed pre-signatures from pool
|
|
164
|
+
*/
|
|
165
|
+
cleanupConsumed(): void {
|
|
166
|
+
for (const [id, preSignature] of this.preSignaturePool.entries()) {
|
|
167
|
+
if (preSignature.consumed) {
|
|
168
|
+
this.preSignaturePool.delete(id);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generate unique ID
|
|
175
|
+
*/
|
|
176
|
+
private generateId(): string {
|
|
177
|
+
return `presig-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Filter messages for broadcast
|
|
182
|
+
*/
|
|
183
|
+
private filterMessages(msgs: Message[], party: number): Message[] {
|
|
184
|
+
return msgs.filter((m) => m.from_id !== party).map(m => m.clone());
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Select P2P messages
|
|
189
|
+
*/
|
|
190
|
+
private selectMessages(msgs: Message[], party: number): Message[] {
|
|
191
|
+
return msgs.filter((m) => m.to_id === party).map(m => m.clone());
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Export singleton instance
|
|
196
|
+
export const preSignatureManager = new PreSignatureManager();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire message format utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type { WireMessage } from '../core/types';
|
|
6
|
+
import { DKLSService } from '../core/DKLSService';
|
|
7
|
+
|
|
8
|
+
// Re-export wire format methods
|
|
9
|
+
export const toWireMessage = DKLSService.toWireMessage;
|
|
10
|
+
export const fromWireMessage = DKLSService.fromWireMessage;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire message validation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { WireMessage } from '../core/types';
|
|
6
|
+
|
|
7
|
+
export function validateWireMessage(message: any): message is WireMessage {
|
|
8
|
+
return (
|
|
9
|
+
typeof message === 'object' &&
|
|
10
|
+
typeof message.from_id === 'number' &&
|
|
11
|
+
typeof message.payload === 'string' &&
|
|
12
|
+
(message.to_id === undefined || typeof message.to_id === 'number')
|
|
13
|
+
);
|
|
14
|
+
}
|