|
6 | 6 | - [Content Format V0](#content-format-v0) |
7 | 7 | - [Content Format V1](#content-format-v1) |
8 | 8 | - [Content Format V2](#content-format-v2) |
| 9 | + - [Content Format V3](#content-format-v3) |
| 10 | + - [Content Format V4](#content-format-v4) |
9 | 11 | - [Payload Format](#supported-payload-versions) |
10 | 12 | - [Payload Format V0](#payload-format-v0) |
11 | 13 | - [Payload Format V1](#payload-format-v1) |
12 | 14 | - [Payload Format V2](#payload-format-v2) |
| 15 | + - [Payload Format V3](#payload-format-v3) |
| 16 | + - [Payload Format V4](#payload-format-v4) |
13 | 17 |
|
14 | 18 | ## Content Format |
15 | 19 |
|
|
182 | 186 | - **2 byte**: Length of `refresh_token` field (set to `0`). |
183 | 187 | - **Variable**: Value of `from` field (considered the test ID). |
184 | 188 |
|
| 189 | +### Content Format V3 |
| 190 | + |
| 191 | +> [!NOTE] |
| 192 | +> |
| 193 | +> For detailed instructions on encrypting the content format using the Double Ratchet algorithm, refer to the [smswithoutborders_lib_sig documentation](https://github.com/smswithoutborders/lib_signal_double_ratchet_python?tab=readme-ov-file#double-ratchet-implementations). |
| 194 | +
|
| 195 | +> [!NOTE] |
| 196 | +> |
| 197 | +> This format improves efficiency by only including length fields for applicable fields per service type, rather than including all fields with zero lengths. |
| 198 | +> |
| 199 | +> **All 2-byte length fields are encoded as unsigned little-endian.** |
| 200 | +
|
| 201 | +1. **Email format**: Binary-encoded fields with the following structure: |
| 202 | + |
| 203 | + - **1 byte**: Length of `from` field. |
| 204 | + - **2 bytes**: Length of `to` field. |
| 205 | + - **2 bytes**: Length of `cc` field. |
| 206 | + - **2 bytes**: Length of `bcc` field. |
| 207 | + - **1 byte**: Length of `subject` field. |
| 208 | + - **2 bytes**: Length of `body` field. |
| 209 | + - **2 bytes**: Length of `access_token` field (optional). |
| 210 | + - **2 bytes**: Length of `refresh_token` field (optional). |
| 211 | + - **Variable**: Value of `from` field. |
| 212 | + - **Variable**: Value of `to` field. |
| 213 | + - **Variable**: Value of `cc` field. |
| 214 | + - **Variable**: Value of `bcc` field. |
| 215 | + - **Variable**: Value of `subject` field. |
| 216 | + - **Variable**: Value of `body` field. |
| 217 | + - **Variable**: Value of `access_token` field (if present). |
| 218 | + - **Variable**: Value of `refresh_token` field (if present). |
| 219 | + |
| 220 | +2. **Text format**: Binary-encoded fields with the following structure: |
| 221 | + |
| 222 | + - **1 byte**: Length of `from` field. |
| 223 | + - **2 bytes**: Length of `body` field. |
| 224 | + - **2 bytes**: Length of `access_token` field (optional). |
| 225 | + - **2 bytes**: Length of `refresh_token` field (optional). |
| 226 | + - **Variable**: Value of `from` field. |
| 227 | + - **Variable**: Value of `body` field. |
| 228 | + - **Variable**: Value of `access_token` field (if present). |
| 229 | + - **Variable**: Value of `refresh_token` field (if present). |
| 230 | + |
| 231 | +3. **Message format**: Binary-encoded fields with the following structure: |
| 232 | + |
| 233 | + - **1 byte**: Length of `from` field. |
| 234 | + - **2 bytes**: Length of `to` field. |
| 235 | + - **2 bytes**: Length of `body` field. |
| 236 | + - **Variable**: Value of `from` field. |
| 237 | + - **Variable**: Value of `to` field. |
| 238 | + - **Variable**: Value of `body` field. |
| 239 | + |
| 240 | +4. **Test format**: Binary-encoded fields with the following structure: |
| 241 | + |
| 242 | + - **1 byte**: Length of `from` field. |
| 243 | + - **Variable**: Value of `from` field (considered the test ID). |
| 244 | + |
| 245 | +### Content Format V4 |
| 246 | + |
| 247 | +> [!NOTE] |
| 248 | +> |
| 249 | +> For detailed instructions on encrypting the content format using the Double Ratchet algorithm, refer to the [smswithoutborders_lib_sig documentation](https://github.com/smswithoutborders/lib_signal_double_ratchet_python?tab=readme-ov-file#double-ratchet-implementations). |
| 250 | +
|
| 251 | +> [!NOTE] |
| 252 | +> |
| 253 | +> This format extends V3 by adding support for image content. A content type indicator determines which content types are present. |
| 254 | +> |
| 255 | +> **All 2-byte length fields are encoded as unsigned little-endian.** |
| 256 | +
|
| 257 | +#### Content Type Indicator |
| 258 | + |
| 259 | +The content type indicator is a single byte with the following values: |
| 260 | + |
| 261 | +- **0**: Text-only content |
| 262 | +- **1**: Image-only content |
| 263 | +- **2**: Text and image content |
| 264 | + |
| 265 | +1. **Email format**: Binary-encoded fields with the following structure: |
| 266 | + |
| 267 | + - **1 byte**: Content type indicator |
| 268 | + - **1 byte**: Length of `from` field (if text content present). |
| 269 | + - **2 bytes**: Length of `to` field (if text content present). |
| 270 | + - **2 bytes**: Length of `cc` field (if text content present). |
| 271 | + - **2 bytes**: Length of `bcc` field (if text content present). |
| 272 | + - **1 byte**: Length of `subject` field (if text content present). |
| 273 | + - **2 bytes**: Length of `body` field (if text content present). |
| 274 | + - **2 bytes**: Length of `access_token` field (if text content present and optional). |
| 275 | + - **2 bytes**: Length of `refresh_token` field (if text content present and optional). |
| 276 | + - **2 bytes**: `Image Session ID` (if image content present). |
| 277 | + - **1 byte**: `Image Segment info` (if image content present). |
| 278 | + - **2 bytes**: Length of `Image` (if image content present). |
| 279 | + - **Variable**: Value of `from` field (if text content present). |
| 280 | + - **Variable**: Value of `to` field (if text content present). |
| 281 | + - **Variable**: Value of `cc` field (if text content present). |
| 282 | + - **Variable**: Value of `bcc` field (if text content present). |
| 283 | + - **Variable**: Value of `subject` field (if text content present). |
| 284 | + - **Variable**: Value of `body` field (if text content present). |
| 285 | + - **Variable**: Value of `access_token` field (if text content present and token provided). |
| 286 | + - **Variable**: Value of `refresh_token` field (if text content present and token provided). |
| 287 | + - **Variable**: Value of `Image` (if image content present). |
| 288 | + |
| 289 | +2. **Text format**: Binary-encoded fields with the following structure: |
| 290 | + |
| 291 | + - **1 byte**: Content type indicator |
| 292 | + - **1 byte**: Length of `from` field (if text content present). |
| 293 | + - **2 bytes**: Length of `body` field (if text content present). |
| 294 | + - **2 bytes**: Length of `access_token` field (if text content present and optional). |
| 295 | + - **2 bytes**: Length of `refresh_token` field (if text content present and optional). |
| 296 | + - **2 bytes**: `Image Session ID` (if image content present). |
| 297 | + - **1 byte**: `Image Segment info` (if image content present). |
| 298 | + - **2 bytes**: Length of `Image` (if image content present). |
| 299 | + - **Variable**: Value of `from` field (if text content present). |
| 300 | + - **Variable**: Value of `body` field (if text content present). |
| 301 | + - **Variable**: Value of `access_token` field (if text content present and token provided). |
| 302 | + - **Variable**: Value of `refresh_token` field (if text content present and token provided). |
| 303 | + - **Variable**: Value of `Image` (if image content present). |
| 304 | + |
| 305 | +3. **Message format**: Binary-encoded fields with the following structure: |
| 306 | + |
| 307 | + - **1 byte**: Content type indicator |
| 308 | + - **1 byte**: Length of `from` field (if text content present). |
| 309 | + - **2 bytes**: Length of `to` field (if text content present). |
| 310 | + - **2 bytes**: Length of `body` field (if text content present). |
| 311 | + - **2 bytes**: `Image Session ID` (if image content present). |
| 312 | + - **1 byte**: `Image Segment info` (if image content present). |
| 313 | + - **2 bytes**: Length of `Image` (if image content present). |
| 314 | + - **Variable**: Value of `from` field (if text content present). |
| 315 | + - **Variable**: Value of `to` field (if text content present). |
| 316 | + - **Variable**: Value of `body` field (if text content present). |
| 317 | + - **Variable**: Value of `Image` (if image content present). |
| 318 | + |
| 319 | +4. **Test format**: Binary-encoded fields with the following structure: |
| 320 | + |
| 321 | + - **1 byte**: Length of `from` field (if text content present). |
| 322 | + - **Variable**: Value of `from` field (considered the test ID, if text content present). |
| 323 | + |
185 | 324 | ## Supported Payload Versions |
186 | 325 |
|
187 | | -| **Version** | **Hexadecimal Value** | **Decimal Value** | **Description** | |
188 | | -| ------------------------ | --------------------- | ----------------- | ----------------------------------------------------------- | |
189 | | -| [v0](#payload-format-v0) | `None` | `None` | No explicit version marker, backward-compatible formats. | |
190 | | -| [v1](#payload-format-v1) | `0x01` | `1` | Includes a version marker as the first byte of the payload. | |
191 | | -| [v2](#payload-format-v1) | `0x02` | `2` | Includes a version marker as the first byte of the payload. | |
| 326 | +| **Version** | **Hexadecimal Value** | **Decimal Value** | **Description** | |
| 327 | +| ------------------------ | --------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
| 328 | +| [v0](#payload-format-v0) | `None` | `None` | Legacy format with 4-byte ciphertext length and no version marker. Device ID has variable length without explicit length field. | |
| 329 | +| [v1](#payload-format-v1) | `0x01` | `1` | Introduces version marker, 2-byte ciphertext length, explicit device ID length field, and language code support. Uses Content Format V1 with 1-byte token length fields. | |
| 330 | +| [v2](#payload-format-v2) | `0x02` | `2` | Same payload structure as V1 but uses Content Format V2 with 2-byte token length fields for better support of longer access/refresh tokens. | |
| 331 | +| [v3](#payload-format-v3) | `0x03` | `3` | Same payload structure as V1/V2 but uses Content Format V3 with bitmap-based field presence indication for more efficient encoding. | |
| 332 | +| [v4](#payload-format-v4) | `0x04` | `4` | Same payload structure as V1/V2/V3 but uses Content Format V4 with support for image content. | |
192 | 333 |
|
193 | 334 | ## Payload Format V0 |
194 | 335 |
|
@@ -320,3 +461,99 @@ payload = ( |
320 | 461 | encoded = base64.b64encode(payload).decode("utf-8") |
321 | 462 | print(encoded) |
322 | 463 | ``` |
| 464 | + |
| 465 | +## Payload Format V3 |
| 466 | + |
| 467 | +> [See available versions](#supported-payload-versions) |
| 468 | +
|
| 469 | +### Message Payload |
| 470 | + |
| 471 | +- **Format**: |
| 472 | + - **1 byte**: Version Marker. [See available versions](#supported-payload-versions). |
| 473 | + - **2 bytes**: Ciphertext Length. |
| 474 | + - **1 byte**: Device ID Length. |
| 475 | + - **1 byte**: Platform shortcode. |
| 476 | + - **Variable**: Ciphertext. (encrypted [Content Format V3](#content-format-v3)). |
| 477 | + - **Variable**: Device ID. |
| 478 | + - **2 bytes**: Language Code (ISO 639-1 format). |
| 479 | + |
| 480 | +> [!NOTE] |
| 481 | +> |
| 482 | +> For detailed instructions on using the Double Ratchet algorithm to create ciphertext, refer to the [smswithoutborders_lib_sig documentation](https://github.com/smswithoutborders/lib_signal_double_ratchet_python?tab=readme-ov-file#double-ratchet-implementations). |
| 483 | +
|
| 484 | +#### Visual Representation: |
| 485 | + |
| 486 | +```plaintext |
| 487 | ++----------------+-------------------+------------------+--------------------+-----------------+-----------------+---------------+ |
| 488 | +| Version Marker | Ciphertext Length | Device ID Length | Platform shortcode | Ciphertext | Device ID | Language Code | |
| 489 | +| (1 byte) | (2 bytes) | (1 byte) | (1 byte) | (Variable size) | (Variable size) | (2 bytes) | |
| 490 | ++----------------+-------------------+------------------+--------------------+-----------------+-----------------+---------------+ |
| 491 | +``` |
| 492 | + |
| 493 | +```python |
| 494 | +version_marker = b'\x03' |
| 495 | +platform_shortcode = b'g' |
| 496 | +language_code = b'en' |
| 497 | +device_id = b'...' |
| 498 | +encrypted_content = b'...' |
| 499 | + |
| 500 | +payload = ( |
| 501 | + version_marker + |
| 502 | + struct.pack("<H", len(encrypted_content)) + |
| 503 | + bytes([len(device_id)]) + |
| 504 | + platform_shortcode + |
| 505 | + encrypted_content + |
| 506 | + device_id + |
| 507 | + language_code |
| 508 | +) |
| 509 | +encoded = base64.b64encode(payload).decode("utf-8") |
| 510 | +print(encoded) |
| 511 | +``` |
| 512 | + |
| 513 | +## Payload Format V4 |
| 514 | + |
| 515 | +> [See available versions](#supported-payload-versions) |
| 516 | +
|
| 517 | +### Message Payload |
| 518 | + |
| 519 | +- **Format**: |
| 520 | + - **1 byte**: Version Marker. [See available versions](#supported-payload-versions). |
| 521 | + - **2 bytes**: Ciphertext Length. |
| 522 | + - **1 byte**: Device ID Length. |
| 523 | + - **1 byte**: Platform shortcode. |
| 524 | + - **Variable**: Ciphertext. (encrypted [Content Format V4](#content-format-v4)). |
| 525 | + - **Variable**: Device ID. |
| 526 | + - **2 bytes**: Language Code (ISO 639-1 format). |
| 527 | + |
| 528 | +> [!NOTE] |
| 529 | +> |
| 530 | +> For detailed instructions on using the Double Ratchet algorithm to create ciphertext, refer to the [smswithoutborders_lib_sig documentation](https://github.com/smswithoutborders/lib_signal_double_ratchet_python?tab=readme-ov-file#double-ratchet-implementations). |
| 531 | +
|
| 532 | +#### Visual Representation: |
| 533 | + |
| 534 | +```plaintext |
| 535 | ++----------------+-------------------+------------------+--------------------+-----------------+-----------------+---------------+ |
| 536 | +| Version Marker | Ciphertext Length | Device ID Length | Platform shortcode | Ciphertext | Device ID | Language Code | |
| 537 | +| (1 byte) | (2 bytes) | (1 byte) | (1 byte) | (Variable size) | (Variable size) | (2 bytes) | |
| 538 | ++----------------+-------------------+------------------+--------------------+-----------------+-----------------+---------------+ |
| 539 | +``` |
| 540 | + |
| 541 | +```python |
| 542 | +version_marker = b'\x04' |
| 543 | +platform_shortcode = b'g' |
| 544 | +language_code = b'en' |
| 545 | +device_id = b'...' |
| 546 | +encrypted_content = b'...' |
| 547 | + |
| 548 | +payload = ( |
| 549 | + version_marker + |
| 550 | + struct.pack("<H", len(encrypted_content)) + |
| 551 | + bytes([len(device_id)]) + |
| 552 | + platform_shortcode + |
| 553 | + encrypted_content + |
| 554 | + device_id + |
| 555 | + language_code |
| 556 | +) |
| 557 | +encoded = base64.b64encode(payload).decode("utf-8") |
| 558 | +print(encoded) |
| 559 | +``` |
0 commit comments