Skip to content

Commit de35c64

Browse files
committed
feat(specification): add Content Format V4 and Payload Format V4 with extended bitmap support
1 parent 85ee868 commit de35c64

1 file changed

Lines changed: 143 additions & 0 deletions

File tree

docs/specification.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
- [Content Format V1](#content-format-v1)
88
- [Content Format V2](#content-format-v2)
99
- [Content Format V3](#content-format-v3)
10+
- [Content Format V4](#content-format-v4)
1011
- [Payload Format](#supported-payload-versions)
1112
- [Payload Format V0](#payload-format-v0)
1213
- [Payload Format V1](#payload-format-v1)
1314
- [Payload Format V2](#payload-format-v2)
1415
- [Payload Format V3](#payload-format-v3)
16+
- [Payload Format V4](#payload-format-v4)
1517

1618
## Content Format
1719

@@ -257,6 +259,98 @@ The bitmap is a single byte where each bit represents the presence of a specific
257259
- **1 byte**: Length of `from` field (if bit 0 is set)
258260
- **Variable**: Value of `from` field (considered the test ID, if present)
259261

262+
### Content Format V4
263+
264+
> [!NOTE]
265+
>
266+
> 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).
267+
268+
> [!NOTE]
269+
>
270+
> This format extends V3 by using a 2-byte bitmap instead of 1-byte, with bits 8 and 9 representing content types.
271+
>
272+
> **All 2-byte length fields are encoded as unsigned little-endian.**
273+
274+
#### Field Bitmap Definition (2 bytes)
275+
276+
The bitmap is 2 bytes where each bit represents the presence of a specific field:
277+
278+
**First byte (bits 0-7) - Standard fields:**
279+
280+
- **Bit 0 (0x01)**: `from` field
281+
- **Bit 1 (0x02)**: `to` field
282+
- **Bit 2 (0x04)**: `cc` field
283+
- **Bit 3 (0x08)**: `bcc` field
284+
- **Bit 4 (0x10)**: `subject` field
285+
- **Bit 5 (0x20)**: `body` field
286+
- **Bit 6 (0x40)**: `access_token` field
287+
- **Bit 7 (0x80)**: `refresh_token` field
288+
289+
**Second byte (bits 8-15) - Content type:**
290+
291+
- **Bit 8 (0x0100)**: Text content present
292+
- **Bit 9 (0x0200)**: Image content present
293+
294+
1. **Email format**: Binary-encoded fields with the following structure:
295+
296+
- **2 bytes**: Field and content type bitmap
297+
- **1 byte**: Length of `from` field (if bit 0 is set and bit 8 is set)
298+
- **2 bytes**: Length of `to` field (if bit 1 is set and bit 8 is set)
299+
- **2 bytes**: Length of `cc` field (if bit 2 is set and bit 8 is set)
300+
- **2 bytes**: Length of `bcc` field (if bit 3 is set and bit 8 is set)
301+
- **1 byte**: Length of `subject` field (if bit 4 is set and bit 8 is set)
302+
- **2 bytes**: Length of `body` field (if bit 5 is set and bit 8 is set)
303+
- **2 bytes**: Length of `access_token` field (if bit 6 is set and bit 8 is set)
304+
- **2 bytes**: Length of `refresh_token` field (if bit 7 is set and bit 8 is set)
305+
- **2 bytes**: `Image Session ID` (if bit 9 is set)
306+
- **1 byte**: `Image Segment info` (if bit 9 is set)
307+
- **2 bytes**: Length of `Image` (if bit 9 is set)
308+
- **Variable**: Value of `from` field (if present)
309+
- **Variable**: Value of `to` field (if present)
310+
- **Variable**: Value of `cc` field (if present)
311+
- **Variable**: Value of `bcc` field (if present)
312+
- **Variable**: Value of `subject` field (if present)
313+
- **Variable**: Value of `body` field (if present)
314+
- **Variable**: Value of `access_token` field (if present)
315+
- **Variable**: Value of `refresh_token` field (if present)
316+
- **Variable**: Value of `Image` (if present)
317+
318+
2. **Text format**: Binary-encoded fields with the following structure:
319+
320+
- **2 bytes**: Field and content type bitmap
321+
- **1 byte**: Length of `from` field (if bit 0 is set and bit 8 is set)
322+
- **2 bytes**: Length of `body` field (if bit 5 is set and bit 8 is set)
323+
- **2 bytes**: Length of `access_token` field (if bit 6 is set and bit 8 is set)
324+
- **2 bytes**: Length of `refresh_token` field (if bit 7 is set and bit 8 is set)
325+
- **2 bytes**: `Image Session ID` (if bit 9 is set)
326+
- **1 byte**: `Image Segment info` (if bit 9 is set)
327+
- **2 bytes**: Length of `Image` (if bit 9 is set)
328+
- **Variable**: Value of `from` field (if present)
329+
- **Variable**: Value of `body` field (if present)
330+
- **Variable**: Value of `access_token` field (if present)
331+
- **Variable**: Value of `refresh_token` field (if present)
332+
- **Variable**: Value of `Image` (if present)
333+
334+
3. **Message format**: Binary-encoded fields with the following structure:
335+
336+
- **2 bytes**: Field and content type bitmap
337+
- **1 byte**: Length of `from` field (if bit 0 is set and bit 8 is set)
338+
- **2 bytes**: Length of `to` field (if bit 1 is set and bit 8 is set)
339+
- **2 bytes**: Length of `body` field (if bit 5 is set and bit 8 is set)
340+
- **2 bytes**: `Image Session ID` (if bit 9 is set)
341+
- **1 byte**: `Image Segment info` (if bit 9 is set)
342+
- **2 bytes**: Length of `Image` (if bit 9 is set)
343+
- **Variable**: Value of `from` field (if present)
344+
- **Variable**: Value of `to` field (if present)
345+
- **Variable**: Value of `body` field (if present)
346+
- **Variable**: Value of `Image` (if present)
347+
348+
4. **Test format**: Binary-encoded fields with the following structure:
349+
350+
- **2 bytes**: Field and content type bitmap
351+
- **1 byte**: Length of `from` field (if bit 0 is set)
352+
- **Variable**: Value of `from` field (considered the test ID, if present)
353+
260354
## Supported Payload Versions
261355

262356
| **Version** | **Hexadecimal Value** | **Decimal Value** | **Description** |
@@ -265,6 +359,7 @@ The bitmap is a single byte where each bit represents the presence of a specific
265359
| [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. |
266360
| [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. |
267361
| [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. |
362+
| [v4](#payload-format-v4) | `0x04` | `4` | Same payload structure as V1/V2/V3 but uses Content Format V4 with 2-byte bitmap supporting image content. |
268363

269364
## Payload Format V0
270365

@@ -444,3 +539,51 @@ payload = (
444539
encoded = base64.b64encode(payload).decode("utf-8")
445540
print(encoded)
446541
```
542+
543+
## Payload Format V4
544+
545+
> [See available versions](#supported-payload-versions)
546+
547+
### Message Payload
548+
549+
- **Format**:
550+
- **1 byte**: Version Marker. [See available versions](#supported-payload-versions).
551+
- **2 bytes**: Ciphertext Length.
552+
- **1 byte**: Device ID Length.
553+
- **1 byte**: Platform shortcode.
554+
- **Variable**: Ciphertext. (encrypted [Content Format V4](#content-format-v4)).
555+
- **Variable**: Device ID.
556+
- **2 bytes**: Language Code (ISO 639-1 format).
557+
558+
> [!NOTE]
559+
>
560+
> 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).
561+
562+
#### Visual Representation:
563+
564+
```plaintext
565+
+----------------+-------------------+------------------+--------------------+-----------------+-----------------+---------------+
566+
| Version Marker | Ciphertext Length | Device ID Length | Platform shortcode | Ciphertext | Device ID | Language Code |
567+
| (1 byte) | (2 bytes) | (1 byte) | (1 byte) | (Variable size) | (Variable size) | (2 bytes) |
568+
+----------------+-------------------+------------------+--------------------+-----------------+-----------------+---------------+
569+
```
570+
571+
```python
572+
version_marker = b'\x04'
573+
platform_shortcode = b'g'
574+
language_code = b'en'
575+
device_id = b'...'
576+
encrypted_content = b'...'
577+
578+
payload = (
579+
version_marker +
580+
struct.pack("<H", len(encrypted_content)) +
581+
bytes([len(device_id)]) +
582+
platform_shortcode +
583+
encrypted_content +
584+
device_id +
585+
language_code
586+
)
587+
encoded = base64.b64encode(payload).decode("utf-8")
588+
print(encoded)
589+
```

0 commit comments

Comments
 (0)