fb-messenger-e2ee 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/DOCS.md +310 -0
- package/LICENSE +660 -0
- package/README.md +153 -0
- package/bun.lock +1014 -0
- package/examples/basic-usage.ts +52 -0
- package/jest.config.ts +18 -0
- package/package.json +56 -0
- package/src/config/env.ts +15 -0
- package/src/controllers/client.controller.ts +805 -0
- package/src/controllers/dgw-handler.ts +171 -0
- package/src/controllers/e2ee-handler.ts +832 -0
- package/src/controllers/event-mapper.ts +327 -0
- package/src/core/client.ts +151 -0
- package/src/e2ee/application/e2ee-client.ts +376 -0
- package/src/e2ee/application/fanout-planner.ts +79 -0
- package/src/e2ee/application/outbound-message-cache.ts +58 -0
- package/src/e2ee/application/prekey-maintenance.ts +55 -0
- package/src/e2ee/application/retry-manager.ts +156 -0
- package/src/e2ee/facebook/facebook-protocol-utils.ts +230 -0
- package/src/e2ee/facebook/icdc-payload.ts +31 -0
- package/src/e2ee/index.ts +76 -0
- package/src/e2ee/internal.ts +27 -0
- package/src/e2ee/media/media-crypto.ts +147 -0
- package/src/e2ee/media/media-upload.ts +90 -0
- package/src/e2ee/message/builders/client-payload.ts +54 -0
- package/src/e2ee/message/builders/consumer-application.ts +362 -0
- package/src/e2ee/message/builders/message-application.ts +64 -0
- package/src/e2ee/message/builders/message-transport.ts +84 -0
- package/src/e2ee/message/codecs/protobuf-codecs.ts +101 -0
- package/src/e2ee/message/constants.ts +4 -0
- package/src/e2ee/message/message-builder.ts +15 -0
- package/src/e2ee/message/proto/ArmadilloApplication.proto +281 -0
- package/src/e2ee/message/proto/ArmadilloICDC.proto +14 -0
- package/src/e2ee/message/proto/ConsumerApplication.proto +232 -0
- package/src/e2ee/message/proto/MessageApplication.proto +82 -0
- package/src/e2ee/message/proto/MessageTransport.proto +77 -0
- package/src/e2ee/message/proto/WACommon.proto +66 -0
- package/src/e2ee/message/proto/WAMediaTransport.proto +176 -0
- package/src/e2ee/message/proto/proto-writer.ts +76 -0
- package/src/e2ee/signal/prekey-manager.ts +140 -0
- package/src/e2ee/signal/signal-manager.ts +365 -0
- package/src/e2ee/store/device-json.ts +47 -0
- package/src/e2ee/store/device-repository.ts +12 -0
- package/src/e2ee/store/device-store.ts +538 -0
- package/src/e2ee/transport/binary/decoder.ts +180 -0
- package/src/e2ee/transport/binary/encoder.ts +143 -0
- package/src/e2ee/transport/binary/stanzas.ts +97 -0
- package/src/e2ee/transport/binary/tokens.ts +64 -0
- package/src/e2ee/transport/binary/wa-binary.ts +8 -0
- package/src/e2ee/transport/dgw/dgw-socket.ts +345 -0
- package/src/e2ee/transport/noise/noise-handshake.ts +417 -0
- package/src/e2ee/transport/noise/noise-socket.ts +230 -0
- package/src/index.ts +34 -0
- package/src/models/client.ts +26 -0
- package/src/models/config.ts +14 -0
- package/src/models/domain.ts +299 -0
- package/src/models/e2ee.ts +295 -0
- package/src/models/media.ts +41 -0
- package/src/models/messaging.ts +88 -0
- package/src/models/thread.ts +69 -0
- package/src/repositories/session.repository.ts +20 -0
- package/src/services/auth.service.ts +55 -0
- package/src/services/e2ee.service.ts +174 -0
- package/src/services/facebook-gateway.service.ts +245 -0
- package/src/services/icdc.service.ts +177 -0
- package/src/services/media.service.ts +304 -0
- package/src/services/messaging.service.ts +28 -0
- package/src/services/thread.service.ts +199 -0
- package/src/types/advanced-types.ts +61 -0
- package/src/types/fca-unofficial.d.ts +212 -0
- package/src/types/protocol-types.ts +43 -0
- package/src/utils/fca-utils.ts +30 -0
- package/src/utils/logger.ts +24 -0
- package/src/utils/mime.ts +51 -0
- package/tests/.env.example +87 -0
- package/tests/data/1x1.png +0 -0
- package/tests/data/example.txt +1 -0
- package/tests/data/file_example.mp3 +0 -0
- package/tests/data/file_example.mp4 +0 -0
- package/tests/integration.test.ts +498 -0
- package/tests/script/echo-e2ee.ts +174 -0
- package/tests/script/send-e2ee-media.ts +227 -0
- package/tests/script/send-e2ee-reaction.ts +108 -0
- package/tests/script/send-e2ee-unsend.ts +115 -0
- package/tests/script/send-typing.ts +107 -0
- package/tests/script/test-group-send.ts +105 -0
- package/tests/setup.ts +3 -0
- package/tests/types.test.ts +57 -0
- package/tests/unit/controllers/dgw-handler.test.ts +60 -0
- package/tests/unit/controllers/e2ee-handler.test.ts +293 -0
- package/tests/unit/controllers/event-mapper.test.ts +252 -0
- package/tests/unit/e2ee/application-helpers.test.ts +418 -0
- package/tests/unit/e2ee/device-store.test.ts +126 -0
- package/tests/unit/e2ee/facebook-protocol-utils.test.ts +134 -0
- package/tests/unit/e2ee/media-crypto.test.ts +55 -0
- package/tests/unit/e2ee/media-upload.test.ts +60 -0
- package/tests/unit/e2ee/message-builder.test.ts +42 -0
- package/tests/unit/e2ee/message-builders.test.ts +230 -0
- package/tests/unit/e2ee/noise-handshake.test.ts +260 -0
- package/tests/unit/e2ee/prekey-manager.test.ts +55 -0
- package/tests/unit/e2ee/wa-binary.test.ts +127 -0
- package/tests/unit/services/e2ee.service.test.ts +101 -0
- package/tests/unit/services/facebook-gateway.service.test.ts +138 -0
- package/tests/unit/services/media.service.test.ts +169 -0
- package/tests/unit/utils/fca-utils.test.ts +48 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { MessengerEvent, Platform } from "./domain.ts";
|
|
2
|
+
|
|
3
|
+
export interface ClientOptions {
|
|
4
|
+
appStatePath?: string;
|
|
5
|
+
appState?: any[] | string;
|
|
6
|
+
sessionStorePath?: string;
|
|
7
|
+
platform?: Platform;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SessionData {
|
|
11
|
+
userId: string;
|
|
12
|
+
appState: Array<{ key: string; value: string }>;
|
|
13
|
+
platform: Platform;
|
|
14
|
+
updatedAt: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SessionRepository {
|
|
18
|
+
read(path: string): Promise<SessionData | null>;
|
|
19
|
+
write(path: string, session: SessionData): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type MessengerEventMap = {
|
|
23
|
+
[E in MessengerEvent as E["type"]]: E["data"];
|
|
24
|
+
} & {
|
|
25
|
+
event: MessengerEvent;
|
|
26
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Platform } from "./domain.ts";
|
|
2
|
+
|
|
3
|
+
export interface AppEnv {
|
|
4
|
+
appStatePath?: string;
|
|
5
|
+
appState?: any[] | string;
|
|
6
|
+
sessionStorePath?: string;
|
|
7
|
+
platform: Platform;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AuthConfig {
|
|
11
|
+
appStatePath?: string;
|
|
12
|
+
appState?: any[] | string;
|
|
13
|
+
platform: Platform;
|
|
14
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
export type Platform = "facebook" | "messenger";
|
|
2
|
+
|
|
3
|
+
// Auth / Session
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// Attachment (mirrors bridge-go Attachment struct)
|
|
7
|
+
|
|
8
|
+
export type AttachmentType = "image" | "video" | "audio" | "voice" | "file" | "sticker" | "gif" | "location" | "link" | "photo";
|
|
9
|
+
|
|
10
|
+
export interface BaseAttachment {
|
|
11
|
+
url?: string;
|
|
12
|
+
fileName?: string;
|
|
13
|
+
mimeType?: string;
|
|
14
|
+
fileSize?: number;
|
|
15
|
+
/** For E2EE media download - base64-encoded */
|
|
16
|
+
mediaKey?: string;
|
|
17
|
+
mediaSha256?: string;
|
|
18
|
+
mediaEncSha256?: string;
|
|
19
|
+
directPath?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ImageAttachment extends BaseAttachment {
|
|
23
|
+
type: "image" | "gif" | "photo";
|
|
24
|
+
width?: number;
|
|
25
|
+
height?: number;
|
|
26
|
+
previewUrl?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface VideoAttachment extends BaseAttachment {
|
|
30
|
+
type: "video";
|
|
31
|
+
width?: number;
|
|
32
|
+
height?: number;
|
|
33
|
+
duration?: number;
|
|
34
|
+
previewUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AudioAttachment extends BaseAttachment {
|
|
38
|
+
type: "audio" | "voice";
|
|
39
|
+
duration?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface FileAttachment extends BaseAttachment {
|
|
43
|
+
type: "file";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface StickerAttachment extends BaseAttachment {
|
|
47
|
+
type: "sticker";
|
|
48
|
+
stickerID: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface LocationAttachment extends BaseAttachment {
|
|
52
|
+
type: "location";
|
|
53
|
+
latitude: number;
|
|
54
|
+
longitude: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface LinkAttachment extends BaseAttachment {
|
|
58
|
+
type: "link";
|
|
59
|
+
description?: string;
|
|
60
|
+
sourceText?: string;
|
|
61
|
+
previewUrl?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type Attachment =
|
|
65
|
+
| ImageAttachment
|
|
66
|
+
| VideoAttachment
|
|
67
|
+
| AudioAttachment
|
|
68
|
+
| FileAttachment
|
|
69
|
+
| StickerAttachment
|
|
70
|
+
| LocationAttachment
|
|
71
|
+
| LinkAttachment;
|
|
72
|
+
|
|
73
|
+
// Mention / ReplyTo
|
|
74
|
+
|
|
75
|
+
export interface Mention {
|
|
76
|
+
userId: string;
|
|
77
|
+
/** UTF-16 code unit offset in the message text */
|
|
78
|
+
offset: number;
|
|
79
|
+
/** UTF-16 code unit length */
|
|
80
|
+
length: number;
|
|
81
|
+
/** "user" | "thread" | "group" */
|
|
82
|
+
type?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ReplyTo {
|
|
86
|
+
messageId: string;
|
|
87
|
+
senderId?: string;
|
|
88
|
+
text?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Thread / UserInfo
|
|
92
|
+
|
|
93
|
+
export interface Thread {
|
|
94
|
+
id: string;
|
|
95
|
+
/** 1 = DM, 2 = group */
|
|
96
|
+
type: number;
|
|
97
|
+
name: string;
|
|
98
|
+
lastActivityTimestampMs: number;
|
|
99
|
+
snippet?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface UserInfo {
|
|
103
|
+
id: string;
|
|
104
|
+
name: string;
|
|
105
|
+
firstName?: string;
|
|
106
|
+
username?: string;
|
|
107
|
+
profilePictureUrl?: string;
|
|
108
|
+
isMessengerUser?: boolean;
|
|
109
|
+
isVerified?: boolean;
|
|
110
|
+
gender?: number;
|
|
111
|
+
canViewerMessage?: boolean;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Messages
|
|
115
|
+
|
|
116
|
+
export interface MessengerMessage {
|
|
117
|
+
id: string;
|
|
118
|
+
threadId: string;
|
|
119
|
+
senderId: string;
|
|
120
|
+
text: string;
|
|
121
|
+
timestampMs: number;
|
|
122
|
+
replyTo?: ReplyTo;
|
|
123
|
+
attachments?: Attachment[];
|
|
124
|
+
mentions?: Mention[];
|
|
125
|
+
isAdminMsg?: boolean;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export type E2EEMessageKind =
|
|
129
|
+
| "text"
|
|
130
|
+
| "image"
|
|
131
|
+
| "video"
|
|
132
|
+
| "audio"
|
|
133
|
+
| "document"
|
|
134
|
+
| "sticker"
|
|
135
|
+
| "reaction"
|
|
136
|
+
| "edit"
|
|
137
|
+
| "revoke"
|
|
138
|
+
| "unknown";
|
|
139
|
+
|
|
140
|
+
export interface BaseE2EEMessage {
|
|
141
|
+
id: string;
|
|
142
|
+
/** Stable conversation ID. For DMs this is the bare Facebook user ID; for groups it is the group JID. */
|
|
143
|
+
threadId: string;
|
|
144
|
+
/** Canonical E2EE chat JID. For DMs this uses the bare .0 Messenger device; senderJid keeps the actual sender device. */
|
|
145
|
+
chatJid: string;
|
|
146
|
+
/** Device-specific sender JID, e.g. user.device@msgr. */
|
|
147
|
+
senderJid: string;
|
|
148
|
+
/** Bare Facebook sender/user ID. */
|
|
149
|
+
senderId: string;
|
|
150
|
+
senderDeviceId?: number;
|
|
151
|
+
isGroup: boolean;
|
|
152
|
+
kind: E2EEMessageKind;
|
|
153
|
+
/** Text body when present; empty string for non-text E2EE events. */
|
|
154
|
+
text: string;
|
|
155
|
+
timestampMs: number;
|
|
156
|
+
attachments?: Attachment[];
|
|
157
|
+
replyTo?: ReplyTo;
|
|
158
|
+
mentions?: Mention[];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface E2EETextMessage extends BaseE2EEMessage {
|
|
162
|
+
kind: "text";
|
|
163
|
+
text: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface E2EEMediaMessage extends BaseE2EEMessage {
|
|
167
|
+
kind: "image" | "video" | "audio" | "document" | "sticker";
|
|
168
|
+
media?: unknown;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface E2EEReactionMessage extends BaseE2EEMessage {
|
|
172
|
+
kind: "reaction";
|
|
173
|
+
reaction: string;
|
|
174
|
+
targetId?: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface E2EEEditMessage extends BaseE2EEMessage {
|
|
178
|
+
kind: "edit";
|
|
179
|
+
targetId?: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface E2EERevokeMessage extends BaseE2EEMessage {
|
|
183
|
+
kind: "revoke";
|
|
184
|
+
targetId?: string;
|
|
185
|
+
fromMe?: boolean;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface E2EEUnknownMessage extends BaseE2EEMessage {
|
|
189
|
+
kind: "unknown";
|
|
190
|
+
raw?: unknown;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export type E2EEMessage =
|
|
194
|
+
| E2EETextMessage
|
|
195
|
+
| E2EEMediaMessage
|
|
196
|
+
| E2EEReactionMessage
|
|
197
|
+
| E2EEEditMessage
|
|
198
|
+
| E2EERevokeMessage
|
|
199
|
+
| E2EEUnknownMessage;
|
|
200
|
+
|
|
201
|
+
// Input types - messaging
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
// Input types - thread management (mirrors media.go)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
// Input types - media download
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
// Events
|
|
212
|
+
|
|
213
|
+
export type MessengerEvent =
|
|
214
|
+
| { type: "message"; data: MessengerMessage }
|
|
215
|
+
| {
|
|
216
|
+
type: "messageEdit";
|
|
217
|
+
data: {
|
|
218
|
+
messageId: string;
|
|
219
|
+
threadId: string;
|
|
220
|
+
newText: string;
|
|
221
|
+
editCount: number;
|
|
222
|
+
timestampMs: number;
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
| {
|
|
226
|
+
type: "reaction";
|
|
227
|
+
data: {
|
|
228
|
+
messageId: string;
|
|
229
|
+
threadId: string;
|
|
230
|
+
actorId: string;
|
|
231
|
+
/** Empty string = reaction removed */
|
|
232
|
+
reaction: string;
|
|
233
|
+
timestampMs: number;
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
| {
|
|
237
|
+
type: "typing";
|
|
238
|
+
data: {
|
|
239
|
+
threadId: string;
|
|
240
|
+
senderId: string;
|
|
241
|
+
isTyping: boolean;
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
| {
|
|
245
|
+
type: "message_unsend";
|
|
246
|
+
data: {
|
|
247
|
+
messageId: string;
|
|
248
|
+
threadId: string;
|
|
249
|
+
actorId: string;
|
|
250
|
+
timestampMs: number;
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
| {
|
|
254
|
+
type: "read_receipt";
|
|
255
|
+
data: {
|
|
256
|
+
threadId: string;
|
|
257
|
+
readerId: string;
|
|
258
|
+
readWatermarkTimestampMs?: number;
|
|
259
|
+
timestampMs: number;
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
| {
|
|
263
|
+
type: "presence";
|
|
264
|
+
data: {
|
|
265
|
+
userId: string;
|
|
266
|
+
isOnline: boolean;
|
|
267
|
+
lastActiveTimestampMs?: number;
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
| { type: "e2ee_connected"; data: Record<string, never> }
|
|
271
|
+
| { type: "e2ee_message"; data: E2EEMessage }
|
|
272
|
+
| {
|
|
273
|
+
type: "e2ee_reaction";
|
|
274
|
+
data: {
|
|
275
|
+
messageId: string;
|
|
276
|
+
chatJid: string;
|
|
277
|
+
senderJid: string;
|
|
278
|
+
senderId: string;
|
|
279
|
+
/** Empty string = unreaction */
|
|
280
|
+
reaction: string;
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
| {
|
|
284
|
+
type: "e2ee_receipt";
|
|
285
|
+
data: {
|
|
286
|
+
type: string;
|
|
287
|
+
chat: string;
|
|
288
|
+
sender: string;
|
|
289
|
+
messageIds: string[];
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
| {
|
|
293
|
+
type: "disconnected";
|
|
294
|
+
data: { isE2EE?: boolean };
|
|
295
|
+
}
|
|
296
|
+
| { type: "reconnected"; data: Record<string, never> }
|
|
297
|
+
| { type: "ready"; data: { isNewSession: boolean } }
|
|
298
|
+
| { type: "raw"; data: Record<string, unknown> }
|
|
299
|
+
| { type: "error"; data: { message: string; code?: number } };
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PreKeyRecord,
|
|
3
|
+
SignedPreKeyRecord,
|
|
4
|
+
KyberPreKeyRecord,
|
|
5
|
+
} from "@signalapp/libsignal-client";
|
|
6
|
+
import type { MediaTypeKey } from "../e2ee/media/media-crypto.ts";
|
|
7
|
+
import type { MmsTypeStr } from "./media.ts";
|
|
8
|
+
|
|
9
|
+
// Storage & Keys
|
|
10
|
+
|
|
11
|
+
export type ProtocolAddressStr = `${string}:${number}`;
|
|
12
|
+
export type SenderKeyId = `${ProtocolAddressStr}:${string}`;
|
|
13
|
+
|
|
14
|
+
export interface DeviceJSON {
|
|
15
|
+
/** schema version for local migrations; absent in old stores */
|
|
16
|
+
schema_version?: number;
|
|
17
|
+
/** base64, 32 bytes - Noise handshake key */
|
|
18
|
+
noise_key_priv: string;
|
|
19
|
+
/** base64, 32 bytes - Signal identity key */
|
|
20
|
+
identity_key_priv: string;
|
|
21
|
+
/** base64, 32 bytes - Signal signed prekey */
|
|
22
|
+
signed_pre_key_priv: string;
|
|
23
|
+
signed_pre_key_id: number;
|
|
24
|
+
/** base64, 64 bytes - Ed25519 sig of signed prekey by identity key */
|
|
25
|
+
signed_pre_key_sig: string;
|
|
26
|
+
registration_id: number;
|
|
27
|
+
/** base64, 32 bytes */
|
|
28
|
+
adv_secret_key: string;
|
|
29
|
+
/** uuid v4 */
|
|
30
|
+
facebook_uuid: string;
|
|
31
|
+
jid_user?: string;
|
|
32
|
+
jid_device?: number;
|
|
33
|
+
/** address -> base64(32B identity pub key) */
|
|
34
|
+
identities?: Record<string, string>;
|
|
35
|
+
/** address -> base64(serialized SessionRecord) */
|
|
36
|
+
sessions?: Record<string, string>;
|
|
37
|
+
/** id(number) -> base64(32B priv key) */
|
|
38
|
+
pre_keys?: Record<string, string>;
|
|
39
|
+
/** "groupJID:senderAddress" -> base64(serialized SenderKeyRecord) */
|
|
40
|
+
sender_keys?: Record<string, string>;
|
|
41
|
+
/** id(number) -> base64(serialized SignedPreKeyRecord) */
|
|
42
|
+
signed_pre_keys?: Record<string, string>;
|
|
43
|
+
/** next prekey ID to generate */
|
|
44
|
+
next_pre_key_id: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface NoiseKeyPair {
|
|
48
|
+
priv: Buffer;
|
|
49
|
+
pub: Buffer;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// PreKeys
|
|
53
|
+
|
|
54
|
+
export interface GeneratedPreKey {
|
|
55
|
+
id: number;
|
|
56
|
+
record: PreKeyRecord;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface PreKeyUploadPayload {
|
|
60
|
+
registrationId: number;
|
|
61
|
+
identityKey: Uint8Array; // 32 bytes, X25519 pub
|
|
62
|
+
signedPreKey: {
|
|
63
|
+
keyId: number;
|
|
64
|
+
publicKey: Uint8Array; // 32 bytes
|
|
65
|
+
signature: Uint8Array; // 64 bytes
|
|
66
|
+
};
|
|
67
|
+
preKeys: Array<{
|
|
68
|
+
keyId: number;
|
|
69
|
+
publicKey: Uint8Array; // 32 bytes
|
|
70
|
+
}>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface RawPreKeyBundle {
|
|
74
|
+
registrationId: number;
|
|
75
|
+
deviceId: number;
|
|
76
|
+
identityKey: Uint8Array; // 33 bytes (serialize() DER compressed)
|
|
77
|
+
signedPreKey: {
|
|
78
|
+
keyId: number;
|
|
79
|
+
publicKey: Uint8Array;
|
|
80
|
+
signature: Uint8Array;
|
|
81
|
+
};
|
|
82
|
+
/** Optional: one-time prekey (may be absent if server ran out) */
|
|
83
|
+
preKey?: {
|
|
84
|
+
keyId: number;
|
|
85
|
+
publicKey: Uint8Array;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Optional: Kyber (PQ) prekey.
|
|
89
|
+
*/
|
|
90
|
+
kyberPreKey?: {
|
|
91
|
+
keyId: number;
|
|
92
|
+
publicKey: Uint8Array;
|
|
93
|
+
signature: Uint8Array;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Handshake & Socket
|
|
98
|
+
|
|
99
|
+
export interface NoiseSocket {
|
|
100
|
+
sendFrame(data: Buffer): Promise<void>;
|
|
101
|
+
readFrame(): Promise<Buffer>;
|
|
102
|
+
close(): void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface RawWebSocket {
|
|
106
|
+
send(data: Buffer): void;
|
|
107
|
+
readRaw(len?: number): Promise<Buffer>;
|
|
108
|
+
close(): void;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface HandshakeResult {
|
|
112
|
+
socket: NoiseSocket;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Client Options & Results
|
|
116
|
+
|
|
117
|
+
export interface E2EESendTextOptions {
|
|
118
|
+
/** Recipient JID (DM) or group JID */
|
|
119
|
+
toJid: string;
|
|
120
|
+
text: string;
|
|
121
|
+
isGroup: boolean;
|
|
122
|
+
/** Own JID - required for all sends (self address in Signal protocol) */
|
|
123
|
+
selfJid: string;
|
|
124
|
+
replyToId?: string;
|
|
125
|
+
replyToSenderJid?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface E2EESendTextResult {
|
|
129
|
+
/** Encrypted MessageTransport bytes - feed into Signal cipher */
|
|
130
|
+
plaintext: Buffer;
|
|
131
|
+
/** frankingTag for the message node */
|
|
132
|
+
frankingTag: Buffer;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface E2EEEncryptMediaOptions {
|
|
136
|
+
type: MediaTypeKey;
|
|
137
|
+
data: Buffer;
|
|
138
|
+
mmsType: MmsTypeStr; // From media-upload.ts
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface E2EEEncryptMediaResult {
|
|
142
|
+
mediaKey: Buffer;
|
|
143
|
+
fileSHA256: Buffer;
|
|
144
|
+
fileEncSHA256: Buffer;
|
|
145
|
+
fileLength: number;
|
|
146
|
+
directPath: string;
|
|
147
|
+
/** Upload handle returned by the media CDN. Useful for parity with native clients. */
|
|
148
|
+
handle: string;
|
|
149
|
+
/** CDN object ID returned by upload; encoded into WAMediaTransport ancillary data. */
|
|
150
|
+
objectId: string;
|
|
151
|
+
/** Pre-built MediaFields for encodeImageMessage / encodeVideoMessage / etc. */
|
|
152
|
+
mediaFields: Omit<MediaFields, "caption" | "ptt" | "fileName">;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface E2EEDecryptMediaOptions {
|
|
156
|
+
data: Buffer;
|
|
157
|
+
mediaKey: Buffer;
|
|
158
|
+
type: MediaTypeKey;
|
|
159
|
+
fileSHA256?: Buffer;
|
|
160
|
+
fileEncSHA256?: Buffer;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export type EncryptionResult =
|
|
164
|
+
| { type: "dm"; encrypted: { type: "msg" | "pkmsg"; ciphertext: Buffer }; frankingTag: Buffer; messageApp: Buffer }
|
|
165
|
+
| {
|
|
166
|
+
type: "group";
|
|
167
|
+
messageApp: Buffer;
|
|
168
|
+
groupCiphertext: Buffer;
|
|
169
|
+
devicePayload: Buffer;
|
|
170
|
+
selfDevicePayload: Buffer;
|
|
171
|
+
skdmPayload: Buffer;
|
|
172
|
+
skdm: { groupId: string; skdmBytes: Buffer; distributionId: string };
|
|
173
|
+
frankingTag: Buffer;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Message Builder
|
|
177
|
+
|
|
178
|
+
export interface MediaFields {
|
|
179
|
+
caption?: string;
|
|
180
|
+
mimeType: string;
|
|
181
|
+
fileSHA256: Buffer;
|
|
182
|
+
fileLength: number;
|
|
183
|
+
mediaKey: Buffer;
|
|
184
|
+
fileEncSHA256: Buffer;
|
|
185
|
+
directPath: string;
|
|
186
|
+
/** Optional CDN object ID from media upload response. */
|
|
187
|
+
objectId?: string;
|
|
188
|
+
/** Unix timestamp seconds for the media key. Defaults to current time when encoding. */
|
|
189
|
+
mediaKeyTimestamp?: number;
|
|
190
|
+
width?: number;
|
|
191
|
+
height?: number;
|
|
192
|
+
seconds?: number;
|
|
193
|
+
ptt?: boolean;
|
|
194
|
+
fileName?: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface MessageTransportOptions {
|
|
198
|
+
/** Application payload. Omit for SKDM-only device fanout. */
|
|
199
|
+
messageApp?: Buffer;
|
|
200
|
+
/** Included only when sending a copy to own other devices */
|
|
201
|
+
dsm?: {
|
|
202
|
+
destinationJid: string;
|
|
203
|
+
phash: string;
|
|
204
|
+
};
|
|
205
|
+
/** Included only for group messages */
|
|
206
|
+
skdm?: {
|
|
207
|
+
groupId: string;
|
|
208
|
+
skdmBytes: Buffer;
|
|
209
|
+
};
|
|
210
|
+
/** Included for group sends with backup directive metadata. */
|
|
211
|
+
backupDirective?: {
|
|
212
|
+
messageId: string;
|
|
213
|
+
actionType?: "UPSERT" | "REMOVE";
|
|
214
|
+
};
|
|
215
|
+
padding?: Buffer;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// E2EEService types
|
|
219
|
+
|
|
220
|
+
export interface E2EEUploadResult {
|
|
221
|
+
messageId: string;
|
|
222
|
+
timestampMs: number;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface E2EEDownloadResult {
|
|
226
|
+
data: Buffer;
|
|
227
|
+
mimeType: string;
|
|
228
|
+
fileSize: number;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface E2EESendImageOptions {
|
|
232
|
+
chatJid: string;
|
|
233
|
+
data: Buffer;
|
|
234
|
+
mimeType?: string;
|
|
235
|
+
caption?: string;
|
|
236
|
+
width?: number;
|
|
237
|
+
height?: number;
|
|
238
|
+
replyToId?: string;
|
|
239
|
+
replyToSenderJid?: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface E2EESendVideoOptions {
|
|
243
|
+
chatJid: string;
|
|
244
|
+
data: Buffer;
|
|
245
|
+
mimeType?: string;
|
|
246
|
+
caption?: string;
|
|
247
|
+
width?: number;
|
|
248
|
+
height?: number;
|
|
249
|
+
duration?: number;
|
|
250
|
+
replyToId?: string;
|
|
251
|
+
replyToSenderJid?: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface E2EESendAudioOptions {
|
|
255
|
+
chatJid: string;
|
|
256
|
+
data: Buffer;
|
|
257
|
+
mimeType?: string;
|
|
258
|
+
duration?: number;
|
|
259
|
+
ptt?: boolean;
|
|
260
|
+
replyToId?: string;
|
|
261
|
+
replyToSenderJid?: string;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface E2EESendDocumentOptions {
|
|
265
|
+
chatJid: string;
|
|
266
|
+
data: Buffer;
|
|
267
|
+
fileName: string;
|
|
268
|
+
mimeType?: string;
|
|
269
|
+
replyToId?: string;
|
|
270
|
+
replyToSenderJid?: string;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export interface E2EESendStickerOptions {
|
|
274
|
+
chatJid: string;
|
|
275
|
+
data: Buffer;
|
|
276
|
+
mimeType?: string;
|
|
277
|
+
width?: number;
|
|
278
|
+
height?: number;
|
|
279
|
+
replyToId?: string;
|
|
280
|
+
replyToSenderJid?: string;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface E2EEDownloadOptions {
|
|
284
|
+
directPath: string;
|
|
285
|
+
/** Base64-encoded */
|
|
286
|
+
mediaKey: string;
|
|
287
|
+
/** Base64-encoded */
|
|
288
|
+
mediaSha256: string;
|
|
289
|
+
/** Base64-encoded */
|
|
290
|
+
mediaEncSha256?: string;
|
|
291
|
+
/** "image" | "video" | "audio" | "voice" | "document" | "sticker" */
|
|
292
|
+
mediaType: string;
|
|
293
|
+
mimeType?: string;
|
|
294
|
+
fileSize?: number;
|
|
295
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface MediaKeys {
|
|
2
|
+
iv: Buffer;
|
|
3
|
+
cipherKey: Buffer;
|
|
4
|
+
macKey: Buffer;
|
|
5
|
+
refKey: Buffer;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface EncryptMediaResult {
|
|
9
|
+
dataToUpload: Buffer;
|
|
10
|
+
fileSHA256: Buffer;
|
|
11
|
+
fileEncSHA256: Buffer;
|
|
12
|
+
fileLength: number;
|
|
13
|
+
mediaKey: Buffer;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface DecryptMediaOptions {
|
|
17
|
+
data: Buffer;
|
|
18
|
+
mediaKey: Buffer;
|
|
19
|
+
type: any; // MediaTypeKey (avoiding circular dep for now)
|
|
20
|
+
fileSHA256?: Buffer;
|
|
21
|
+
fileEncSHA256?: Buffer;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MediaUploadConfig {
|
|
25
|
+
/** From server's media connection response */
|
|
26
|
+
auth: string;
|
|
27
|
+
/** Host to upload to - Messenger prefers the last host (rupload.facebook.com) */
|
|
28
|
+
host: string;
|
|
29
|
+
ttl?: number;
|
|
30
|
+
authTtl?: number;
|
|
31
|
+
fetchedAtMs?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface MediaUploadResult {
|
|
35
|
+
url: string;
|
|
36
|
+
directPath: string;
|
|
37
|
+
handle: string;
|
|
38
|
+
objectId: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type MmsTypeStr = "image" | "video" | "ptt" | "document" | "sticker";
|