bitchat-android/docs/PEER-ID-ROTATION-ANDROID.md
2026-08-04 19:58:19 +02:00

3.6 KiB

Peer ID Rotation: Android Phase 1

This document records Android's implementation and review of the cross-platform peer-ID rotation proposal. Phase 1 is intentionally additive: Android advertises capability bit 14, parses message type 0x2C, and provides tested cryptographic primitives. It still emits v1 announcements and ignores v2 announcements after strict parsing. No rotating identity is used by the shipping mesh yet.

Android review

  • O1 — one-hour epochs: accept as the first interoperable constant. It is a reasonable compromise for rollout, and pinning it now is better than adding negotiation before operational data exists. Changing it later is a protocol revision because it moves every vector.
  • O4 — unsigned v2 announcements: acceptable only with the proposed trust boundary. A parsed announcement must not establish liveness or enter the people list until a recognition-tag match or a completed Noise handshake. Phase 1 therefore parses and discards it, and does not gossip-relay it.
  • O5 — rotation during live sessions: prefer migration at the epoch boundary, but do not implement it until stable fingerprint-keyed session and durable-state migration exists. Indefinite deferral would create the strongest tracking handle for the most active peers.
  • O7 — trailing bytes: Android's outer BinaryProtocol decoder currently accepts bytes after the declared packet fields; an executable test pins this. This does not make unilateral padding changes safe because padding participates in signed canonical bytes. Padding changes still need capability/version gating.

Reproduced section 7 vectors

The Android JVM tests reproduce the three published vectors directly from the specification:

rotationSecret = fb82dfec0c0a2a4677beca44e2f72c80e7c5de773dd5fce6ee47af83d3c25f09
peerID(epoch=100) = f7c08c528506a374
tag A->B = 4568f61d61d6cbfb
tag B->A = 5313c7731f629959

The tests also cover a real two-sided X25519 exchange, directional tags, consecutive epochs, the epoch-1/current/epoch+1 window, fixed 64-byte tag blocks, slot-independent matching, and fixed-width binding messages.

Proposed joint vectors

These vectors were generated independently from the prose with Python standard hash/HMAC primitives plus the cryptography Ed25519/X25519 primitives. They do not depend on the iOS implementation.

Binding signature

Inputs:

Ed25519 private seed = 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20
epoch = 100
peer ID = f7c08c528506a374
Noise X25519 public key derived from private bytes 01..20 =
07a37cbc142093c8b755dc1b10e86cb426374ad16aa853ed0bdfc0b2b86d1c7c

Canonical message:

626974636861742d7065657269642d62696e64696e672d763100000064f7c08c528506a37407a37cbc142093c8b755dc1b10e86cb426374ad16aa853ed0bdfc0b2b86d1c7c

Ed25519 signature:

5b1235d4fd4ff0bdef76007578dc83141e34a2014f249aeb5df3e50c7d30199b2fe1c92f7a3674a6170a1b8db3e9213aeac6fa3690fb4b5e7e4432ca69d28a0e

Full announceV2 packet

The packet uses version 1, type 0x2C, TTL 3, timestamp 1700000000000, no flags or signature, sender f7c08c528506a374, epoch 100, the two published directional tags followed by padding bytes 00..2f, capability bit 14 encoded minimal little-endian as 0040, and no geohash.

012c030000018bcfe5680000004cf7c08c528506a37401040000006402404568f61d61d6cbfb5313c7731f629959000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f03020040

The deterministic padding is only for the vector. Production tag padding must use uniformly random bytes and tag order.