@shhhum/xftp-web 0.1.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/README.md +47 -0
- package/dist/agent.d.ts +46 -0
- package/dist/agent.js +273 -0
- package/dist/agent.js.map +1 -0
- package/dist/client.d.ts +63 -0
- package/dist/client.js +353 -0
- package/dist/client.js.map +1 -0
- package/dist/crypto/digest.d.ts +3 -0
- package/dist/crypto/digest.js +23 -0
- package/dist/crypto/digest.js.map +1 -0
- package/dist/crypto/file.d.ts +14 -0
- package/dist/crypto/file.js +68 -0
- package/dist/crypto/file.js.map +1 -0
- package/dist/crypto/identity.d.ts +10 -0
- package/dist/crypto/identity.js +98 -0
- package/dist/crypto/identity.js.map +1 -0
- package/dist/crypto/keys.d.ts +27 -0
- package/dist/crypto/keys.js +138 -0
- package/dist/crypto/keys.js.map +1 -0
- package/dist/crypto/padding.d.ts +8 -0
- package/dist/crypto/padding.js +60 -0
- package/dist/crypto/padding.js.map +1 -0
- package/dist/crypto/secretbox.d.ts +22 -0
- package/dist/crypto/secretbox.js +195 -0
- package/dist/crypto/secretbox.js.map +1 -0
- package/dist/download.d.ts +9 -0
- package/dist/download.js +60 -0
- package/dist/download.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/address.d.ts +7 -0
- package/dist/protocol/address.js +50 -0
- package/dist/protocol/address.js.map +1 -0
- package/dist/protocol/chunks.d.ts +15 -0
- package/dist/protocol/chunks.js +75 -0
- package/dist/protocol/chunks.js.map +1 -0
- package/dist/protocol/client.d.ts +9 -0
- package/dist/protocol/client.js +69 -0
- package/dist/protocol/client.js.map +1 -0
- package/dist/protocol/commands.d.ts +68 -0
- package/dist/protocol/commands.js +115 -0
- package/dist/protocol/commands.js.map +1 -0
- package/dist/protocol/description.d.ts +37 -0
- package/dist/protocol/description.js +317 -0
- package/dist/protocol/description.js.map +1 -0
- package/dist/protocol/encoding.d.ts +34 -0
- package/dist/protocol/encoding.js +197 -0
- package/dist/protocol/encoding.js.map +1 -0
- package/dist/protocol/handshake.d.ts +47 -0
- package/dist/protocol/handshake.js +158 -0
- package/dist/protocol/handshake.js.map +1 -0
- package/dist/protocol/transmission.d.ts +15 -0
- package/dist/protocol/transmission.js +84 -0
- package/dist/protocol/transmission.js.map +1 -0
- package/package.json +40 -0
- package/src/agent.ts +372 -0
- package/src/client.ts +448 -0
- package/src/crypto/digest.ts +26 -0
- package/src/crypto/file.ts +94 -0
- package/src/crypto/identity.ts +112 -0
- package/src/crypto/keys.ts +172 -0
- package/src/crypto/padding.ts +61 -0
- package/src/crypto/secretbox.ts +219 -0
- package/src/download.ts +76 -0
- package/src/index.ts +4 -0
- package/src/protocol/address.ts +54 -0
- package/src/protocol/chunks.ts +86 -0
- package/src/protocol/client.ts +95 -0
- package/src/protocol/commands.ts +157 -0
- package/src/protocol/description.ts +363 -0
- package/src/protocol/encoding.ts +224 -0
- package/src/protocol/handshake.ts +220 -0
- package/src/protocol/transmission.ts +113 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
// XFTP HTTP/2 client -- Simplex.FileTransfer.Client
|
|
2
|
+
//
|
|
3
|
+
// Connects to XFTP server via HTTP/2, performs web handshake,
|
|
4
|
+
// sends authenticated commands, receives responses.
|
|
5
|
+
//
|
|
6
|
+
// Uses node:http2 in Node.js (tests), fetch() in browsers.
|
|
7
|
+
import { encodeAuthTransmission, encodeTransmission, decodeTransmission, XFTP_BLOCK_SIZE, initialXFTPVersion, currentXFTPVersion } from "./protocol/transmission.js";
|
|
8
|
+
import { encodeClientHello, encodeClientHandshake, decodeServerHandshake, compatibleVRange } from "./protocol/handshake.js";
|
|
9
|
+
import { verifyIdentityProof } from "./crypto/identity.js";
|
|
10
|
+
import { generateX25519KeyPair, encodePubKeyX25519, dh } from "./crypto/keys.js";
|
|
11
|
+
import { encodeFNEW, encodeFADD, encodeFPUT, encodeFGET, encodeFDEL, encodePING, decodeResponse } from "./protocol/commands.js";
|
|
12
|
+
import { decryptReceivedChunk } from "./download.js";
|
|
13
|
+
import { formatXFTPServer } from "./protocol/address.js";
|
|
14
|
+
import { concatBytes } from "./protocol/encoding.js";
|
|
15
|
+
import { blockUnpad } from "./protocol/transmission.js";
|
|
16
|
+
// -- Error types
|
|
17
|
+
export class XFTPRetriableError extends Error {
|
|
18
|
+
errorType;
|
|
19
|
+
constructor(errorType) {
|
|
20
|
+
super(humanReadableMessage(errorType));
|
|
21
|
+
this.errorType = errorType;
|
|
22
|
+
this.name = "XFTPRetriableError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class XFTPPermanentError extends Error {
|
|
26
|
+
errorType;
|
|
27
|
+
constructor(errorType, message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.errorType = errorType;
|
|
30
|
+
this.name = "XFTPPermanentError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function isRetriable(e) {
|
|
34
|
+
if (e instanceof XFTPRetriableError)
|
|
35
|
+
return true;
|
|
36
|
+
if (e instanceof XFTPPermanentError)
|
|
37
|
+
return false;
|
|
38
|
+
if (e instanceof TypeError)
|
|
39
|
+
return true; // fetch network error
|
|
40
|
+
if (e instanceof Error && e.name === "AbortError")
|
|
41
|
+
return true; // timeout
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
export function categorizeError(e) {
|
|
45
|
+
if (e instanceof XFTPRetriableError || e instanceof XFTPPermanentError)
|
|
46
|
+
return e;
|
|
47
|
+
if (e instanceof TypeError)
|
|
48
|
+
return new XFTPRetriableError("NETWORK");
|
|
49
|
+
if (e instanceof Error && e.name === "AbortError")
|
|
50
|
+
return new XFTPRetriableError("TIMEOUT");
|
|
51
|
+
return e instanceof Error ? e : new Error(String(e));
|
|
52
|
+
}
|
|
53
|
+
export function humanReadableMessage(errorType) {
|
|
54
|
+
const t = typeof errorType === "string" ? errorType : errorType.type;
|
|
55
|
+
switch (t) {
|
|
56
|
+
case "SESSION": return "Session expired, reconnecting...";
|
|
57
|
+
case "HANDSHAKE": return "Connection interrupted, reconnecting...";
|
|
58
|
+
case "NETWORK": return "Network error, retrying...";
|
|
59
|
+
case "TIMEOUT": return "Server timeout, retrying...";
|
|
60
|
+
case "AUTH": return "File is invalid, expired, or has been removed";
|
|
61
|
+
case "NO_FILE": return "File not found — it may have expired";
|
|
62
|
+
case "SIZE": return "File size exceeds server limit";
|
|
63
|
+
case "QUOTA": return "Server storage quota exceeded";
|
|
64
|
+
case "BLOCKED": return "File has been blocked by server";
|
|
65
|
+
case "DIGEST": return "File integrity check failed";
|
|
66
|
+
case "INTERNAL": return "Server internal error";
|
|
67
|
+
case "CMD": return "Protocol error";
|
|
68
|
+
default: return "Server error: " + t;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const DEFAULT_TRANSPORT_CONFIG = { timeoutMs: 30000 };
|
|
72
|
+
// -- Transport implementations
|
|
73
|
+
const isNode = typeof globalThis.process !== "undefined" && globalThis.process.versions?.node;
|
|
74
|
+
async function createTransport(baseUrl, config) {
|
|
75
|
+
if (isNode) {
|
|
76
|
+
return createNodeTransport(baseUrl, config);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
return createBrowserTransport(baseUrl, config);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function createNodeTransport(baseUrl, config) {
|
|
83
|
+
const http2 = await import("node:http2");
|
|
84
|
+
const session = http2.connect(baseUrl, { rejectUnauthorized: false });
|
|
85
|
+
return {
|
|
86
|
+
async post(body, headers) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
const req = session.request({ ":method": "POST", ":path": "/", ...headers });
|
|
89
|
+
req.setTimeout(config.timeoutMs, () => {
|
|
90
|
+
req.close();
|
|
91
|
+
reject(Object.assign(new Error("Request timeout"), { name: "AbortError" }));
|
|
92
|
+
});
|
|
93
|
+
const chunks = [];
|
|
94
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
95
|
+
req.on("end", () => resolve(new Uint8Array(Buffer.concat(chunks))));
|
|
96
|
+
req.on("error", reject);
|
|
97
|
+
req.end(Buffer.from(body));
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
close() {
|
|
101
|
+
session.close();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function createBrowserTransport(baseUrl, config) {
|
|
106
|
+
// In dev mode, route through /xftp-proxy to avoid self-signed cert rejection
|
|
107
|
+
// __XFTP_PROXY_PORT__ is 'proxy' in dev mode (uses relative path), null in production
|
|
108
|
+
const effectiveUrl = typeof __XFTP_PROXY_PORT__ !== 'undefined' && __XFTP_PROXY_PORT__
|
|
109
|
+
? '/xftp-proxy'
|
|
110
|
+
: baseUrl;
|
|
111
|
+
return {
|
|
112
|
+
async post(body, headers) {
|
|
113
|
+
const controller = new AbortController();
|
|
114
|
+
const timer = setTimeout(() => controller.abort(), config.timeoutMs);
|
|
115
|
+
try {
|
|
116
|
+
const resp = await fetch(effectiveUrl, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers,
|
|
119
|
+
body,
|
|
120
|
+
signal: controller.signal
|
|
121
|
+
});
|
|
122
|
+
if (!resp.ok) {
|
|
123
|
+
console.error('[XFTP] fetch %s failed: %d %s', effectiveUrl, resp.status, resp.statusText);
|
|
124
|
+
throw new Error(`Server request failed: ${resp.status} ${resp.statusText}`);
|
|
125
|
+
}
|
|
126
|
+
return new Uint8Array(await resp.arrayBuffer());
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
clearTimeout(timer);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
close() { }
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export function newXFTPAgent() {
|
|
136
|
+
return { connections: new Map(), _connectFn: connectXFTP };
|
|
137
|
+
}
|
|
138
|
+
export function getXFTPServerClient(agent, server) {
|
|
139
|
+
const key = formatXFTPServer(server);
|
|
140
|
+
let conn = agent.connections.get(key);
|
|
141
|
+
if (!conn) {
|
|
142
|
+
const p = agent._connectFn(server);
|
|
143
|
+
conn = { client: p, queue: Promise.resolve() };
|
|
144
|
+
agent.connections.set(key, conn);
|
|
145
|
+
p.catch(() => {
|
|
146
|
+
const cur = agent.connections.get(key);
|
|
147
|
+
if (cur && cur.client === p)
|
|
148
|
+
agent.connections.delete(key);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return conn.client;
|
|
152
|
+
}
|
|
153
|
+
export function reconnectClient(agent, server) {
|
|
154
|
+
const key = formatXFTPServer(server);
|
|
155
|
+
const old = agent.connections.get(key);
|
|
156
|
+
old?.client.then(c => c.transport.close(), () => { });
|
|
157
|
+
const p = agent._connectFn(server);
|
|
158
|
+
const conn = { client: p, queue: old?.queue ?? Promise.resolve() };
|
|
159
|
+
agent.connections.set(key, conn);
|
|
160
|
+
p.catch(() => {
|
|
161
|
+
const cur = agent.connections.get(key);
|
|
162
|
+
if (cur && cur.client === p)
|
|
163
|
+
agent.connections.delete(key);
|
|
164
|
+
});
|
|
165
|
+
return p;
|
|
166
|
+
}
|
|
167
|
+
export function removeStaleConnection(agent, server, failedP) {
|
|
168
|
+
const key = formatXFTPServer(server);
|
|
169
|
+
const conn = agent.connections.get(key);
|
|
170
|
+
if (conn && conn.client === failedP) {
|
|
171
|
+
agent.connections.delete(key);
|
|
172
|
+
failedP.then(c => c.transport.close(), () => { });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export function closeXFTPServerClient(agent, server) {
|
|
176
|
+
const key = formatXFTPServer(server);
|
|
177
|
+
const conn = agent.connections.get(key);
|
|
178
|
+
if (conn) {
|
|
179
|
+
agent.connections.delete(key);
|
|
180
|
+
conn.client.then(c => c.transport.close(), () => { });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export function closeXFTPAgent(agent) {
|
|
184
|
+
for (const conn of agent.connections.values()) {
|
|
185
|
+
conn.client.then(c => c.transport.close(), () => { });
|
|
186
|
+
}
|
|
187
|
+
agent.connections.clear();
|
|
188
|
+
}
|
|
189
|
+
// -- Connect + handshake
|
|
190
|
+
export async function connectXFTP(server, config) {
|
|
191
|
+
const cfg = { ...DEFAULT_TRANSPORT_CONFIG, ...config };
|
|
192
|
+
const baseUrl = "https://" + server.host + ":" + server.port;
|
|
193
|
+
const transport = await createTransport(baseUrl, cfg);
|
|
194
|
+
try {
|
|
195
|
+
// Step 1: send client hello with web challenge
|
|
196
|
+
const challenge = new Uint8Array(32);
|
|
197
|
+
crypto.getRandomValues(challenge);
|
|
198
|
+
const clientHelloBytes = encodeClientHello({ webChallenge: challenge });
|
|
199
|
+
const shsBody = await transport.post(clientHelloBytes, { "xftp-web-hello": "1" });
|
|
200
|
+
// Step 2: decode + verify server handshake
|
|
201
|
+
const hs = decodeServerHandshake(shsBody);
|
|
202
|
+
if (!hs.webIdentityProof) {
|
|
203
|
+
console.error('[XFTP] Server did not provide web identity proof');
|
|
204
|
+
throw new Error("Server did not provide web identity proof");
|
|
205
|
+
}
|
|
206
|
+
const idOk = verifyIdentityProof({
|
|
207
|
+
certChainDer: hs.certChainDer,
|
|
208
|
+
signedKeyDer: hs.signedKeyDer,
|
|
209
|
+
sigBytes: hs.webIdentityProof,
|
|
210
|
+
challenge,
|
|
211
|
+
sessionId: hs.sessionId,
|
|
212
|
+
keyHash: server.keyHash
|
|
213
|
+
});
|
|
214
|
+
if (!idOk) {
|
|
215
|
+
console.error('[XFTP] Server identity verification failed');
|
|
216
|
+
throw new Error("Server identity verification failed");
|
|
217
|
+
}
|
|
218
|
+
// Step 3: version negotiation
|
|
219
|
+
const vr = compatibleVRange(hs.xftpVersionRange, { minVersion: initialXFTPVersion, maxVersion: currentXFTPVersion });
|
|
220
|
+
if (!vr) {
|
|
221
|
+
console.error('[XFTP] Incompatible server version: %o', hs.xftpVersionRange);
|
|
222
|
+
throw new Error("Incompatible server version");
|
|
223
|
+
}
|
|
224
|
+
const xftpVersion = vr.maxVersion;
|
|
225
|
+
// Step 4: send client handshake
|
|
226
|
+
const ack = await transport.post(encodeClientHandshake({ xftpVersion, keyHash: server.keyHash }), { "xftp-handshake": "1" });
|
|
227
|
+
if (ack.length !== 0) {
|
|
228
|
+
console.error('[XFTP] Non-empty handshake ack (%d bytes)', ack.length);
|
|
229
|
+
throw new Error("Server handshake failed");
|
|
230
|
+
}
|
|
231
|
+
return { baseUrl, sessionId: hs.sessionId, xftpVersion, transport };
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
console.error('[XFTP] Connection to %s failed:', baseUrl, e);
|
|
235
|
+
transport.close();
|
|
236
|
+
throw e;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// -- Send command (single attempt, no retry)
|
|
240
|
+
async function sendXFTPCommandOnce(client, privateKey, entityId, cmdBytes, chunkData) {
|
|
241
|
+
const corrId = new Uint8Array(0);
|
|
242
|
+
const block = encodeAuthTransmission(client.sessionId, corrId, entityId, cmdBytes, privateKey);
|
|
243
|
+
const reqBody = chunkData ? concatBytes(block, chunkData) : block;
|
|
244
|
+
const fullResp = await client.transport.post(reqBody);
|
|
245
|
+
console.log(`[XFTP-DBG] sendOnce: fullResp.length=${fullResp.length} entityId=${_hex(entityId)} cmdTag=${cmdBytes[0]}`);
|
|
246
|
+
if (fullResp.length < XFTP_BLOCK_SIZE) {
|
|
247
|
+
console.error('[XFTP] Response too short: %d bytes (expected >= %d)', fullResp.length, XFTP_BLOCK_SIZE);
|
|
248
|
+
throw new Error("Server response too short");
|
|
249
|
+
}
|
|
250
|
+
const respBlock = fullResp.subarray(0, XFTP_BLOCK_SIZE);
|
|
251
|
+
const body = fullResp.subarray(XFTP_BLOCK_SIZE);
|
|
252
|
+
console.log(`[XFTP-DBG] sendOnce: body.length=${body.length} body.byteOffset=${body.byteOffset} body.buffer.byteLength=${body.buffer.byteLength}`);
|
|
253
|
+
// Detect padded error strings (HANDSHAKE, SESSION) before decodeTransmission
|
|
254
|
+
const raw = blockUnpad(respBlock);
|
|
255
|
+
if (raw.length < 20) {
|
|
256
|
+
const text = new TextDecoder().decode(raw);
|
|
257
|
+
if (/^[A-Z_]+$/.test(text)) {
|
|
258
|
+
throw new XFTPRetriableError(text);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const { command } = decodeTransmission(client.sessionId, respBlock);
|
|
262
|
+
const response = decodeResponse(command);
|
|
263
|
+
if (response.type === "FRErr") {
|
|
264
|
+
const err = response.err;
|
|
265
|
+
if (err.type === "SESSION" || err.type === "HANDSHAKE") {
|
|
266
|
+
throw new XFTPRetriableError(err.type);
|
|
267
|
+
}
|
|
268
|
+
throw new XFTPPermanentError(err.type, humanReadableMessage(err));
|
|
269
|
+
}
|
|
270
|
+
return { response, body };
|
|
271
|
+
}
|
|
272
|
+
function _hex(b, n = 8) {
|
|
273
|
+
return Array.from(b.slice(0, n)).map(x => x.toString(16).padStart(2, '0')).join('');
|
|
274
|
+
}
|
|
275
|
+
// -- Send command (with retry + reconnect)
|
|
276
|
+
export async function sendXFTPCommand(agent, server, privateKey, entityId, cmdBytes, chunkData, maxRetries = 3) {
|
|
277
|
+
let clientP = getXFTPServerClient(agent, server);
|
|
278
|
+
let client = await clientP;
|
|
279
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
280
|
+
try {
|
|
281
|
+
if (attempt > 1)
|
|
282
|
+
console.log(`[XFTP-DBG] sendCmd: retry attempt=${attempt}/${maxRetries}`);
|
|
283
|
+
return await sendXFTPCommandOnce(client, privateKey, entityId, cmdBytes, chunkData);
|
|
284
|
+
}
|
|
285
|
+
catch (e) {
|
|
286
|
+
console.log(`[XFTP-DBG] sendCmd: attempt=${attempt} failed: ${e instanceof Error ? e.message : String(e)} retriable=${isRetriable(e)}`);
|
|
287
|
+
if (!isRetriable(e)) {
|
|
288
|
+
throw categorizeError(e);
|
|
289
|
+
}
|
|
290
|
+
if (attempt === maxRetries) {
|
|
291
|
+
removeStaleConnection(agent, server, clientP);
|
|
292
|
+
throw categorizeError(e);
|
|
293
|
+
}
|
|
294
|
+
clientP = reconnectClient(agent, server);
|
|
295
|
+
client = await clientP;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
throw new Error("unreachable");
|
|
299
|
+
}
|
|
300
|
+
// -- Command wrappers
|
|
301
|
+
export async function createXFTPChunk(agent, server, spKey, file, rcvKeys, auth = null) {
|
|
302
|
+
const { response } = await sendXFTPCommand(agent, server, spKey, new Uint8Array(0), encodeFNEW(file, rcvKeys, auth));
|
|
303
|
+
if (response.type !== "FRSndIds")
|
|
304
|
+
throw new Error("unexpected response: " + response.type);
|
|
305
|
+
return { senderId: response.senderId, recipientIds: response.recipientIds };
|
|
306
|
+
}
|
|
307
|
+
export async function addXFTPRecipients(agent, server, spKey, fId, rcvKeys) {
|
|
308
|
+
const { response } = await sendXFTPCommand(agent, server, spKey, fId, encodeFADD(rcvKeys));
|
|
309
|
+
if (response.type !== "FRRcvIds")
|
|
310
|
+
throw new Error("unexpected response: " + response.type);
|
|
311
|
+
return response.recipientIds;
|
|
312
|
+
}
|
|
313
|
+
export async function uploadXFTPChunk(agent, server, spKey, fId, chunkData) {
|
|
314
|
+
const { response } = await sendXFTPCommand(agent, server, spKey, fId, encodeFPUT(), chunkData);
|
|
315
|
+
if (response.type !== "FROk")
|
|
316
|
+
throw new Error("unexpected response: " + response.type);
|
|
317
|
+
}
|
|
318
|
+
export async function downloadXFTPChunkRaw(agent, server, rpKey, fId) {
|
|
319
|
+
const { publicKey, privateKey } = generateX25519KeyPair();
|
|
320
|
+
const cmd = encodeFGET(encodePubKeyX25519(publicKey));
|
|
321
|
+
const { response, body } = await sendXFTPCommand(agent, server, rpKey, fId, cmd);
|
|
322
|
+
if (response.type !== "FRFile")
|
|
323
|
+
throw new Error("unexpected response: " + response.type);
|
|
324
|
+
const dhSecret = dh(response.rcvDhKey, privateKey);
|
|
325
|
+
console.log(`[XFTP-DBG] dlChunkRaw: body.length=${body.length} nonce=${_hex(response.nonce, 24)} dhSecret=${_hex(dhSecret)} body[0..8]=${_hex(body)} body[-8..]=${_hex(body.slice(-8))}`);
|
|
326
|
+
return { dhSecret, nonce: response.nonce, body };
|
|
327
|
+
}
|
|
328
|
+
export async function downloadXFTPChunk(agent, server, rpKey, fId, digest) {
|
|
329
|
+
const { dhSecret, nonce, body } = await downloadXFTPChunkRaw(agent, server, rpKey, fId);
|
|
330
|
+
return decryptReceivedChunk(dhSecret, nonce, body, digest ?? null);
|
|
331
|
+
}
|
|
332
|
+
export async function deleteXFTPChunk(agent, server, spKey, sId) {
|
|
333
|
+
const { response } = await sendXFTPCommand(agent, server, spKey, sId, encodeFDEL());
|
|
334
|
+
if (response.type !== "FROk")
|
|
335
|
+
throw new Error("unexpected response: " + response.type);
|
|
336
|
+
}
|
|
337
|
+
export async function pingXFTP(agent, server) {
|
|
338
|
+
const client = await getXFTPServerClient(agent, server);
|
|
339
|
+
const corrId = new Uint8Array(0);
|
|
340
|
+
const block = encodeTransmission(client.sessionId, corrId, new Uint8Array(0), encodePING());
|
|
341
|
+
const fullResp = await client.transport.post(block);
|
|
342
|
+
if (fullResp.length < XFTP_BLOCK_SIZE)
|
|
343
|
+
throw new Error("pingXFTP: response too short");
|
|
344
|
+
const { command } = decodeTransmission(client.sessionId, fullResp.subarray(0, XFTP_BLOCK_SIZE));
|
|
345
|
+
const response = decodeResponse(command);
|
|
346
|
+
if (response.type !== "FRPong")
|
|
347
|
+
throw new Error("unexpected response: " + response.type);
|
|
348
|
+
}
|
|
349
|
+
// -- Close
|
|
350
|
+
export function closeXFTP(c) {
|
|
351
|
+
c.transport.close();
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Cryptographic hash functions matching Simplex.Messaging.Crypto (sha256Hash, sha512Hash).
|
|
2
|
+
import sodium from "libsodium-wrappers-sumo";
|
|
3
|
+
// SHA-256 digest (32 bytes) -- Crypto.hs:1006
|
|
4
|
+
export function sha256(data) {
|
|
5
|
+
return sodium.crypto_hash_sha256(data);
|
|
6
|
+
}
|
|
7
|
+
// SHA-512 digest (64 bytes) -- Crypto.hs:1011
|
|
8
|
+
export function sha512(data) {
|
|
9
|
+
return sodium.crypto_hash_sha512(data);
|
|
10
|
+
}
|
|
11
|
+
// Streaming SHA-512 over multiple chunks -- avoids copying large data into WASM memory at once.
|
|
12
|
+
// Internally segments chunks larger than 4MB to limit peak WASM memory usage.
|
|
13
|
+
export function sha512Streaming(chunks) {
|
|
14
|
+
const SEG = 4 * 1024 * 1024;
|
|
15
|
+
const state = sodium.crypto_hash_sha512_init();
|
|
16
|
+
for (const chunk of chunks) {
|
|
17
|
+
for (let off = 0; off < chunk.length; off += SEG) {
|
|
18
|
+
sodium.crypto_hash_sha512_update(state, chunk.subarray(off, Math.min(off + SEG, chunk.length)));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return sodium.crypto_hash_sha512_final(state);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=digest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest.js","sourceRoot":"","sources":["../../src/crypto/digest.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAE3F,OAAO,MAA2B,MAAM,yBAAyB,CAAA;AAEjE,8CAA8C;AAC9C,MAAM,UAAU,MAAM,CAAC,IAAgB;IACrC,OAAO,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAA;AACxC,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,MAAM,CAAC,IAAgB;IACrC,OAAO,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAA;AACxC,CAAC;AAED,gGAAgG;AAChG,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAAC,MAA4B;IAC1D,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,uBAAuB,EAA6B,CAAA;IACzE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;YACjD,MAAM,CAAC,yBAAyB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACjG,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAA;AAC/C,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface FileHeader {
|
|
2
|
+
fileName: string;
|
|
3
|
+
fileExtra: string | null;
|
|
4
|
+
}
|
|
5
|
+
export declare function encodeFileHeader(hdr: FileHeader): Uint8Array;
|
|
6
|
+
export declare function parseFileHeader(data: Uint8Array): {
|
|
7
|
+
header: FileHeader;
|
|
8
|
+
rest: Uint8Array;
|
|
9
|
+
};
|
|
10
|
+
export declare function encryptFile(source: Uint8Array, fileHdr: Uint8Array, key: Uint8Array, nonce: Uint8Array, fileSize: bigint, encSize: bigint): Uint8Array;
|
|
11
|
+
export declare function decryptChunks(encSize: bigint, chunks: Uint8Array[], key: Uint8Array, nonce: Uint8Array): {
|
|
12
|
+
header: FileHeader;
|
|
13
|
+
content: Uint8Array;
|
|
14
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// File-level encryption/decryption matching Simplex.FileTransfer.Crypto.
|
|
2
|
+
// Operates on in-memory Uint8Array (no file I/O needed for browser).
|
|
3
|
+
import { Decoder, concatBytes, encodeInt64, encodeString, decodeString, encodeMaybe, decodeMaybe } from "../protocol/encoding.js";
|
|
4
|
+
import { sbInit, sbEncryptChunk, sbDecryptTailTag, sbAuth } from "./secretbox.js";
|
|
5
|
+
const AUTH_TAG_SIZE = 16n;
|
|
6
|
+
// Encoding matches Haskell: smpEncode (fileName, fileExtra)
|
|
7
|
+
// = smpEncode fileName <> smpEncode fileExtra
|
|
8
|
+
// = encodeString(fileName) + encodeMaybe(encodeString, fileExtra)
|
|
9
|
+
export function encodeFileHeader(hdr) {
|
|
10
|
+
return concatBytes(encodeString(hdr.fileName), encodeMaybe(encodeString, hdr.fileExtra));
|
|
11
|
+
}
|
|
12
|
+
// Parse FileHeader from decrypted content (first 1024 bytes examined).
|
|
13
|
+
// Returns the parsed header and remaining bytes (file content).
|
|
14
|
+
export function parseFileHeader(data) {
|
|
15
|
+
const hdrLen = Math.min(1024, data.length);
|
|
16
|
+
const d = new Decoder(data.subarray(0, hdrLen));
|
|
17
|
+
const fileName = decodeString(d);
|
|
18
|
+
const fileExtra = decodeMaybe(decodeString, d);
|
|
19
|
+
const consumed = d.offset();
|
|
20
|
+
return {
|
|
21
|
+
header: { fileName, fileExtra },
|
|
22
|
+
rest: data.subarray(consumed)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// -- Encryption (FileTransfer.Crypto:encryptFile)
|
|
26
|
+
// Encrypt file content with streaming XSalsa20-Poly1305.
|
|
27
|
+
// Output format: encrypted(Int64 fileSize | fileHdr | source | '#' padding) | 16-byte auth tag
|
|
28
|
+
//
|
|
29
|
+
// source -- raw file content
|
|
30
|
+
// fileHdr -- pre-encoded FileHeader bytes (from encodeFileHeader)
|
|
31
|
+
// key -- 32-byte symmetric key
|
|
32
|
+
// nonce -- 24-byte nonce
|
|
33
|
+
// fileSize -- BigInt(fileHdr.length + source.length)
|
|
34
|
+
// encSize -- total output size (including 16-byte auth tag)
|
|
35
|
+
export function encryptFile(source, fileHdr, key, nonce, fileSize, encSize) {
|
|
36
|
+
const state = sbInit(key, nonce);
|
|
37
|
+
const lenStr = encodeInt64(fileSize);
|
|
38
|
+
const padLen = Number(encSize - AUTH_TAG_SIZE - fileSize - 8n);
|
|
39
|
+
if (padLen < 0)
|
|
40
|
+
throw new Error("encryptFile: encSize too small");
|
|
41
|
+
const hdr = sbEncryptChunk(state, concatBytes(lenStr, fileHdr));
|
|
42
|
+
const encSource = sbEncryptChunk(state, source);
|
|
43
|
+
const padding = new Uint8Array(padLen);
|
|
44
|
+
padding.fill(0x23); // '#'
|
|
45
|
+
const encPad = sbEncryptChunk(state, padding);
|
|
46
|
+
const tag = sbAuth(state);
|
|
47
|
+
return concatBytes(hdr, encSource, encPad, tag);
|
|
48
|
+
}
|
|
49
|
+
// -- Decryption (FileTransfer.Crypto:decryptChunks)
|
|
50
|
+
// Decrypt one or more XFTP chunks into a FileHeader and file content.
|
|
51
|
+
// Chunks are concatenated, then decrypted as a single stream.
|
|
52
|
+
//
|
|
53
|
+
// encSize -- total encrypted size (including 16-byte auth tag)
|
|
54
|
+
// chunks -- downloaded XFTP chunk data (concatenated = full encrypted file)
|
|
55
|
+
// key -- 32-byte symmetric key
|
|
56
|
+
// nonce -- 24-byte nonce
|
|
57
|
+
export function decryptChunks(encSize, chunks, key, nonce) {
|
|
58
|
+
if (chunks.length === 0)
|
|
59
|
+
throw new Error("decryptChunks: empty chunks");
|
|
60
|
+
const paddedLen = encSize - AUTH_TAG_SIZE;
|
|
61
|
+
const data = chunks.length === 1 ? chunks[0] : concatBytes(...chunks);
|
|
62
|
+
const { valid, content } = sbDecryptTailTag(key, nonce, paddedLen, data);
|
|
63
|
+
if (!valid)
|
|
64
|
+
throw new Error("decryptChunks: invalid auth tag");
|
|
65
|
+
const { header, rest } = parseFileHeader(content);
|
|
66
|
+
return { header, content: rest };
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/crypto/file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function extractCertPublicKeyInfo(certDer: Uint8Array): Uint8Array;
|
|
2
|
+
export interface IdentityVerification {
|
|
3
|
+
certChainDer: Uint8Array[];
|
|
4
|
+
signedKeyDer: Uint8Array;
|
|
5
|
+
sigBytes: Uint8Array;
|
|
6
|
+
challenge: Uint8Array;
|
|
7
|
+
sessionId: Uint8Array;
|
|
8
|
+
keyHash: Uint8Array;
|
|
9
|
+
}
|
|
10
|
+
export declare function verifyIdentityProof(v: IdentityVerification): boolean;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// Web handshake identity proof verification.
|
|
2
|
+
//
|
|
3
|
+
// Verifies server identity in the XFTP web handshake using the certificate
|
|
4
|
+
// chain from the protocol handshake (independent of TLS certificates).
|
|
5
|
+
// Ed25519 via libsodium, Ed448 via @noble/curves.
|
|
6
|
+
import { Decoder, concatBytes } from "../protocol/encoding.js";
|
|
7
|
+
import { sha256 } from "./digest.js";
|
|
8
|
+
import { verify, decodePubKeyEd25519, verifyEd448, decodePubKeyEd448 } from "./keys.js";
|
|
9
|
+
import { chainIdCaCerts, extractSignedKey } from "../protocol/handshake.js";
|
|
10
|
+
// -- ASN.1 DER helpers (minimal, for X.509 parsing)
|
|
11
|
+
function derLen(d) {
|
|
12
|
+
const first = d.anyByte();
|
|
13
|
+
if (first < 0x80)
|
|
14
|
+
return first;
|
|
15
|
+
const n = first & 0x7f;
|
|
16
|
+
if (n === 0 || n > 4)
|
|
17
|
+
throw new Error("DER: unsupported length encoding");
|
|
18
|
+
let len = 0;
|
|
19
|
+
for (let i = 0; i < n; i++)
|
|
20
|
+
len = (len << 8) | d.anyByte();
|
|
21
|
+
return len;
|
|
22
|
+
}
|
|
23
|
+
function derSkip(d) {
|
|
24
|
+
d.anyByte();
|
|
25
|
+
d.take(derLen(d));
|
|
26
|
+
}
|
|
27
|
+
function derReadElement(d) {
|
|
28
|
+
const start = d.offset();
|
|
29
|
+
d.anyByte();
|
|
30
|
+
d.take(derLen(d));
|
|
31
|
+
return d.buf.subarray(start, d.offset());
|
|
32
|
+
}
|
|
33
|
+
// -- X.509 certificate public key extraction
|
|
34
|
+
// Extract SubjectPublicKeyInfo DER from a full X.509 certificate DER.
|
|
35
|
+
// Navigates: Certificate -> TBSCertificate -> skip version, serialNumber,
|
|
36
|
+
// signatureAlg, issuer, validity, subject -> SubjectPublicKeyInfo.
|
|
37
|
+
export function extractCertPublicKeyInfo(certDer) {
|
|
38
|
+
const d = new Decoder(certDer);
|
|
39
|
+
if (d.anyByte() !== 0x30)
|
|
40
|
+
throw new Error("X.509: expected Certificate SEQUENCE");
|
|
41
|
+
derLen(d);
|
|
42
|
+
if (d.anyByte() !== 0x30)
|
|
43
|
+
throw new Error("X.509: expected TBSCertificate SEQUENCE");
|
|
44
|
+
derLen(d);
|
|
45
|
+
if (d.buf[d.offset()] === 0xa0)
|
|
46
|
+
derSkip(d); // version [0] EXPLICIT (optional)
|
|
47
|
+
derSkip(d); // serialNumber
|
|
48
|
+
derSkip(d); // signature AlgorithmIdentifier
|
|
49
|
+
derSkip(d); // issuer
|
|
50
|
+
derSkip(d); // validity
|
|
51
|
+
derSkip(d); // subject
|
|
52
|
+
return derReadElement(d); // SubjectPublicKeyInfo
|
|
53
|
+
}
|
|
54
|
+
function detectKeyAlgorithm(spki) {
|
|
55
|
+
if (spki.length === 44 && spki[8] === 0x70)
|
|
56
|
+
return 'ed25519';
|
|
57
|
+
if (spki.length === 69 && spki[8] === 0x71)
|
|
58
|
+
return 'ed448';
|
|
59
|
+
throw new Error("unsupported certificate key algorithm");
|
|
60
|
+
}
|
|
61
|
+
// Extract raw public key from SPKI DER, auto-detecting Ed25519 or Ed448.
|
|
62
|
+
function extractCertRawKey(spki) {
|
|
63
|
+
const alg = detectKeyAlgorithm(spki);
|
|
64
|
+
const key = alg === 'ed25519' ? decodePubKeyEd25519(spki) : decodePubKeyEd448(spki);
|
|
65
|
+
return { key, alg };
|
|
66
|
+
}
|
|
67
|
+
// Verify signature using the appropriate algorithm.
|
|
68
|
+
function verifySig(alg, key, sig, msg) {
|
|
69
|
+
return alg === 'ed25519' ? verify(key, sig, msg) : verifyEd448(key, sig, msg);
|
|
70
|
+
}
|
|
71
|
+
// Verify server identity proof from XFTP web handshake.
|
|
72
|
+
// 1. Certificate chain has valid structure (2-4 certs)
|
|
73
|
+
// 2. SHA-256(idCert) matches expected keyHash
|
|
74
|
+
// 3. Challenge signature valid: verify(leafKey, sigBytes, challenge || sessionId)
|
|
75
|
+
// 4. DH key signature valid: verify(leafKey, signedKey.signature, signedKey.objectDer)
|
|
76
|
+
export function verifyIdentityProof(v) {
|
|
77
|
+
const cc = chainIdCaCerts(v.certChainDer);
|
|
78
|
+
if (cc.type !== 'valid')
|
|
79
|
+
return false;
|
|
80
|
+
const fp = sha256(cc.idCert);
|
|
81
|
+
if (!constantTimeEqual(fp, v.keyHash))
|
|
82
|
+
return false;
|
|
83
|
+
const spki = extractCertPublicKeyInfo(cc.leafCert);
|
|
84
|
+
const { key, alg } = extractCertRawKey(spki);
|
|
85
|
+
if (!verifySig(alg, key, v.sigBytes, concatBytes(v.challenge, v.sessionId)))
|
|
86
|
+
return false;
|
|
87
|
+
const sk = extractSignedKey(v.signedKeyDer);
|
|
88
|
+
return verifySig(alg, key, sk.signature, sk.objectDer);
|
|
89
|
+
}
|
|
90
|
+
function constantTimeEqual(a, b) {
|
|
91
|
+
if (a.length !== b.length)
|
|
92
|
+
return false;
|
|
93
|
+
let diff = 0;
|
|
94
|
+
for (let i = 0; i < a.length; i++)
|
|
95
|
+
diff |= a[i] ^ b[i];
|
|
96
|
+
return diff === 0;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/crypto/identity.ts"],"names":[],"mappings":"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"}
|