wacore 0.7.0

Core WhatsApp protocol implementation without runtime dependencies
Documentation
# STUN protobuf attribute payloads

The WhatsApp VoIP relay Allocate request carries three protobuf-encoded STUN attributes. We do not
vendor the upstream `.proto` files or a protobuf runtime: the encoders in `stun.rs`
(`create_voip_sender_subscriptions`, `create_apk_sender_subscriptions`,
`create_apk_stream_descriptors`) emit the wire bytes directly, and the frozen `stun_proto` KATs in
`kats.json` pin them (`stun.rs::protobuf_payloads_match_kat`).

This doc is the field layout those encoders implement, so the format stays reviewable without the
external proto sources. All KAT vectors use `ssrc = 0x12345678` and (where applicable) `pid = 7`,
`layerId = "audio"`.

## `voip.SenderSubscriptions` (WASM client, STUN attr `0x4000`)

One audio sender. `ssrc` is a plain `uint32` varint.

```
message SenderSubscriptions {       // attr payload
  repeated Sender senders = 1;       // one entry
}
message Sender {
  uint32 ssrc          = 3;          // = 0x12345678
  StreamLayer  stream_layer = 5;     // AUDIO  = 0
  PayloadType  payload_type = 6;     // MEDIA  = 0
}
```

KAT `voip_sender_subscriptions = 0a0a18f8acd1910128003000`:
`0a 0a` senders[0] len=10; `18 f8acd1910 1` field3 ssrc; `28 00` field5=0; `30 00` field6=0.

## `wa.voip.SenderSubscriptions` (APK client, STUN attr `0x4025`)

One audio ssrc, with the ssrc carried as a **zigzag `sint64`**. The optional per-participant `pid`
nests a `layerId` string.

```
message SenderSubscriptions {
  repeated Subscription subscriptions = 1;
}
message Subscription {
  SsrcLayers ssrc_layers = 1;
}
message SsrcLayers {
  repeated sint64 ssrcs = 1;         // ssrcs[0] = zigzag(0x12345678)
  repeated Pid    pids  = 2;         // empty in the nopid KAT
}
message Pid {
  sint64 pid      = 1;               // zigzag(7)
  string layer_id = 2;              // "audio"
}
```

KAT `apk_sender_subscriptions_nopid = 0a08 0a06 08f0d9a2a302`:
subscriptions[0] -> ssrc_layers -> ssrcs[0] = zigzag(0x12345678).

KAT `apk_sender_subscriptions_pid` additionally carries `pids[0] = { pid: zigzag(7), layerId:
"audio" }`.

## `wa.voip.StreamDescriptors` (APK client, STUN attr `0x4024`)

One audio/OPUS descriptor. `ssrc` is a zigzag `sint64`.

```
message StreamDescriptors {
  repeated StreamDescriptor stream_descriptors = 1;
}
message StreamDescriptor {
  string stream_layer                 = 1;   // "audio"
  string payload_type                 = 2;   // "OPUS"
  sint64 ssrc                         = 3;   // zigzag(0x12345678)
  bool   is_uplink_prefetch_enabled   = 4;   // false (0)
}
```

KAT `apk_stream_descriptors = 0a15 0a05 617564696f 12 04 4f505553 18 f0d9a2a302 20 00`:
stream_descriptors[0] -> "audio", "OPUS", ssrc, is_uplink_prefetch_enabled=false.

## Provenance

`kats.json` is a frozen fixture. The crypto vectors (the e2e-srtp / hbh-srtp / sframe key schedules and
the RTP/RTCP IVs) were generated once by an independent reference (standard HKDF-SHA256 + the RFC 3711
AES-128-CTR KDF) — the WhatsApp VoIP key schedule is fixed, so they never change. The Rust impl is
pinned against them byte-for-byte by the KAT tests in `mod.rs` / `e2e_srtp.rs` / `sframe.rs` /
`hbh_srtp.rs`; those tests catch drift in the impl, not in the vectors. If a brand-new crypto vector is
ever needed, compute it with an independent crypto tool (not the impl under test, which would be
circular).

The STUN protobuf vectors (`stun`, `ping`, `apk_*`, `attr_token`) are re-encodable by hand from the
field layout documented above: to change `ssrc`/`pid`, re-encode the payload and update `kats.json`;
the Rust encoders + `protobuf_payloads_match_kat` then re-pin it.