Merge pull request #876 from qutad/feat/announce-capability-bits

feat: Add announce capability bit assignments
This commit is contained in:
callebtc 2026-08-17 21:05:02 +02:00 committed by GitHub
commit 09b481f1ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 5 deletions

View File

@ -29,9 +29,23 @@ data class PeerCapabilities(val rawValue: Long) : Parcelable {
companion object {
val NONE = PeerCapabilities(0)
val PREKEYS = PeerCapabilities(1L shl 0)
val WIFI_BULK = PeerCapabilities(1L shl 1)
val GATEWAY = PeerCapabilities(1L shl 2)
val GROUPS = PeerCapabilities(1L shl 3)
val BOARD = PeerCapabilities(1L shl 4)
val VOUCH = PeerCapabilities(1L shl 5)
val MESH_DIAGNOSTICS = PeerCapabilities(1L shl 6)
val BRIDGE = PeerCapabilities(1L shl 7)
/** Noise-encrypted private BitchatFilePacket using payload type 0x20. */
val PRIVATE_MEDIA = PeerCapabilities(1L shl 8)
val PRIVATE_MEDIA_RECEIPTS = PeerCapabilities(1L shl 9)
/** Reserved by iOS; decode it but do not advertise or act on it. */
val NON_DESTRUCTIVE_NOISE_REPLACEMENT = PeerCapabilities(1L shl 10)
/** Capabilities implemented by this Android build. */
val LOCAL_SUPPORTED = PRIVATE_MEDIA

View File

@ -12,9 +12,38 @@ class IdentityAnnouncementTest {
private val signingKey = ByteArray(32) { 0x22 }
@Test
fun `private media capability uses iOS little-endian bytes`() {
fun `capability assignments and encoding match iOS`() {
assertEquals(1L shl 0, PeerCapabilities.PREKEYS.rawValue)
assertEquals(1L shl 1, PeerCapabilities.WIFI_BULK.rawValue)
assertEquals(1L shl 2, PeerCapabilities.GATEWAY.rawValue)
assertEquals(1L shl 3, PeerCapabilities.GROUPS.rawValue)
assertEquals(1L shl 4, PeerCapabilities.BOARD.rawValue)
assertEquals(1L shl 5, PeerCapabilities.VOUCH.rawValue)
assertEquals(1L shl 6, PeerCapabilities.MESH_DIAGNOSTICS.rawValue)
assertEquals(1L shl 7, PeerCapabilities.BRIDGE.rawValue)
assertEquals(1L shl 8, PeerCapabilities.PRIVATE_MEDIA.rawValue)
assertEquals(1L shl 9, PeerCapabilities.PRIVATE_MEDIA_RECEIPTS.rawValue)
assertEquals(1L shl 10, PeerCapabilities.NON_DESTRUCTIVE_NOISE_REPLACEMENT.rawValue)
val assigned = PeerCapabilities((1L shl 11) - 1)
assertArrayEquals(byteArrayOf(0xFF.toByte(), 0x07), assigned.encoded())
assertEquals(assigned, PeerCapabilities.decode(assigned.encoded()))
}
@Test
fun `capability encoding is minimal and preserves low 64 unknown bits`() {
assertArrayEquals(byteArrayOf(0x00), PeerCapabilities.NONE.encoded())
assertArrayEquals(byteArrayOf(0x01), PeerCapabilities.PREKEYS.encoded())
assertArrayEquals(byteArrayOf(0x00, 0x01), PeerCapabilities.PRIVATE_MEDIA.encoded())
assertTrue(PeerCapabilities.decode(byteArrayOf(0x00, 0x01)).contains(PeerCapabilities.PRIVATE_MEDIA))
val future = PeerCapabilities.decode(
byteArrayOf(0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80.toByte(), 0x55)
)
assertEquals(Long.MIN_VALUE or 1L, future.rawValue)
assertArrayEquals(
byteArrayOf(0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80.toByte()),
future.encoded()
)
}
@Test
@ -71,5 +100,6 @@ class IdentityAnnouncementTest {
.capabilities!!
.contains(PeerCapabilities.PRIVATE_MEDIA)
)
assertEquals(PeerCapabilities.PRIVATE_MEDIA, PeerCapabilities.LOCAL_SUPPORTED)
}
}

View File

@ -29,9 +29,16 @@ Existing TLVs (unchanged):
Other optional extension:
- `0x05` CAPABILITIES: Minimal little-endian feature bitfield. Bit 8 advertises
authenticated Noise private media (`PRIVATE_MEDIA_V1`); its exact value is
`00 01`. An empty value is valid and means no advertised capabilities.
- `0x05` CAPABILITIES: Minimal little-endian feature bitfield. Assigned bits
match iOS: 0 prekeys, 1 Wi-Fi bulk, 2 gateway, 3 private groups, 4 board,
5 vouch, 6 mesh diagnostics, 7 bridge, 8 authenticated Noise private media,
9 private-media receipts, and 10 reserved non-destructive Noise replacement.
Bit 8's exact value is `00 01`. An empty value is valid and means no
advertised capabilities. For this announcement TLV, unknown bits and bytes
beyond the low 64 bits must be tolerated for forward compatibility. This
tolerance does not apply to the Noise-authenticated peer-state payload
`0x21`, whose capability field must be canonical and 1...8 bytes; see
`PRIVATE_MEDIA_V1.md`.
New TLV (optional):
@ -69,6 +76,12 @@ This matches the onwire 8byte `senderID`/`recipientID` encoding used in th
security-sensitive bit is trusted only after the signed announcement key is
bound to the matching remote static key from a live Noise handshake; see
`PRIVATE_MEDIA_V1.md`.
- Capability bits are independent discovery hints. Implementations advertise
only wire features they implement and must not infer support from platform,
app version, or unrelated transports with similar names.
- Private-media receipts and retry (bit 9) require the current Noise
generation's authenticated `0x21` peer state to prove both bits 8 and 9. An
announcement alone never authorizes that behavior.
- If a `0x04` TLV is present:
- Interpret the value as `N = length / 8` peer IDs (ignore trailing nonaligned bytes).
- Each 8byte chunk is decoded back to a 16hexchar peer ID string (lowercase).