Repository navigation
Link setup and AirShield
Link setup is the handshake on service 5. It runs once per link, before any DataX message is encrypted, and it installs the keys that AirShield-frames-and-ciphers uses for every later frame. This page covers the three link-setup messages, the key exchange and the channel binding. It also covers EndLinkSetup and its UUID rule, the protocol phases, and the failures and what they look like on the wire.
Each handshake message is a DataX message sent as a whole SDU. There is no AirShield frame byte in front of it. The first byte is the DataX header, and its top bit (hasExtension) is set because every handshake message carries tags. Only the sealed frames that follow the handshake carry the AirShield type byte.
Link setup makes no network call. Meta's servers take no part in anything on this page.
The phone sends two messages, reads the glasses' two messages, computes the keys, runs trust and only then sends EndLinkSetup. The glasses' own EndLinkSetup comes after ours, and the phone answers it.
sequenceDiagram
participant Phone
participant Glasses
Phone->>Glasses: RequestEncryption (ch 0x8001, svc 5, type 1)
Phone->>Glasses: EnableEncryption (ch 0x0001, type 2)
Glasses-->>Phone: RequestEncryption (ch 0x8001, svc 5, type 1)
Glasses-->>Phone: EnableEncryption (ch 0x0001, type 2)
Note over Phone,Glasses: ECDH and key schedule, nothing sent
Phone->>Glasses: svc36 trust exchange (sealed DATA)
Phone->>Glasses: EndLinkSetup (sealed, ch 0x8001, type 0x1000)
Glasses-->>Phone: EndLinkSetup (sealed, ch 0x8001, type 0x1000)
Phone->>Glasses: status 0x1000 on mirror ch 0x0001 (sealed)
Note over Phone,Glasses: READY
The phone's steps, in order:
- The phone draws an ephemeral P-256 key pair, a 16-byte challenge, a 32-byte seed, a 16-byte IV and a random 32-bit sequence start.
- The phone sends RequestEncryption on channel 0x8001. It is the only handshake message that carries the service tag (5).
- The phone sends EnableEncryption on channel 0x0001 at once. It needs only the phone's own values.
- The phone reads the glasses' messages. It matches them by message type 1 and 2, not by channel. Any DATA frame that arrives in this loop is held until the handshake ends.
- The phone computes the shared secret from its private key and the glasses' EnableEncryption public key.
- The phone derives one key set for each direction.
- The phone keeps the channel binding material. Nothing is sent.
- The phone runs the svc36 trust exchange. Its signature covers the channel binding.
- The phone sends EndLinkSetup as the first sealed frame.
- The phone waits for the glasses' EndLinkSetup and answers it.
- The phone is ready for DataX traffic.
The messages use protobuf field numbers. Varints use wire type 0, and length-delimited bytes use wire type 2.
| Field | Type | Phone sends | Notes |
|---|---|---|---|
| 1 | bytes, 64 | ephemeral public key, x then y, 32 bytes each, no 0x04 prefix |
Big-endian, fixed width |
| 2 | bytes, 16 | challenge | The nonce for the phone's receive keys |
| 3 | varint | 0 | Curve, 0 is secp256r1 |
| 4 | varint | 31 | Supported parameters |
| 5 | bytes, repeated | none | Key hints. The phone sends none |
| 6 | varint | omitted | Quirks, omitted when zero |
| 7 | varint | 16 | AirShield version, see open questions |
In the glasses' RequestEncryption, field 5 holds one key hint and field 7 is 17.
| Field | Type | Phone sends | Notes |
|---|---|---|---|
| 1 | bytes, 64 | ephemeral public key, same as in RequestEncryption | Used for ECDH |
| 2 | bytes, 32 | seed | Random |
| 3 | bytes, 16 | IV | Random, the initial CBC IV for the phone's direction |
| 4 | varint, unsigned 32 bit | base | Random, the initial sequence number for the phone's direction |
| 5 | varint | 31 | Parameters. The glasses drop the phone's frames when this is 1 |
| 6 to 9 | varint | omitted | Quirks and link setup fields, omitted when zero |
The glasses also send fields 7 and 8, phased link setup support and a supported services bitmask. Whether they change the outcome is unknown.
| Field | Type | Value | Notes |
|---|---|---|---|
| 1 | varint | 1 |
asMain, 1 on every connect |
| 2 | bytes, 16 | scope UUID | RFC 4122 version 4, see below |
| 3 | bytes, 16 | lease UUID | RFC 4122 version 4, see below |
The phone omits field 4, repeated user data.
The DataX header is 4 bytes. Each tag adds 4 bytes. The payload length in the header counts everything after the 4 fixed bytes. The table gives the SDU size for each message.
| Message | Channel | Tags | Protobuf bytes | SDU bytes |
|---|---|---|---|---|
| RequestEncryption, no key hint | 0x8001 | svc 5, type 1 | 90 | 102 |
| EnableEncryption | 0x0001 | type 2 | 121 plus the length of field 4 | 129 plus that length |
| EndLinkSetup, plaintext | 0x8001 | type 0x1000 | 38 | 46, then 58 on the wire when sealed |
The EnableEncryption size depends on the random base. The field 4 varint is 1 to 5 bytes. A 5-byte base gives 134 bytes.
RequestEncryption header, 12 bytes:
80 62 80 01 81 00 00 05 02 00 00 01
-
80 62:hasExtensionset, payload length 0x062 = 98 (8 bytes of tags plus 90 bytes of protobuf). -
80 01: channel 0x8001. -
81 00 00 05: tag 1 (service id), continuation bit set, value 5. -
02 00 00 01: tag 2 (message type), last tag, value 1.
EnableEncryption header, 8 bytes, with a 5-byte base:
80 82 00 01 02 00 00 02
The payload length is 0x82 = 130, which is 4 bytes of tags plus 126 bytes of protobuf. There is no service tag.
EndLinkSetup plaintext header and body, 46 bytes:
80 2A 80 01 02 00 10 00
08 01
12 10 <16 bytes scope UUID>
1A 10 <16 bytes lease UUID>
The message type tag holds 0x1000 in its 16-bit value.
Both sides use secp256r1 (NIST P-256). The public key on the wire is 64 bytes, x then y, with no 0x04 prefix. Each coordinate is exactly 32 bytes, big-endian, with leading zeros kept.
The shared secret S is the 32-byte x coordinate of the ECDH point. The phone uses its ephemeral private key and the glasses' EnableEncryption field 1. It does not use the glasses' RequestEncryption key. Each direction then derives its own keys with HKDF over SHA-256:
salt_aes = SHA-256(nonce || seed)
salt_hmac = SHA-256(seed || nonce || "hmac_derive")
PRK_aes = HMAC-SHA-256(key = salt_aes, data = S)
PRK_hmac = HMAC-SHA-256(key = salt_hmac, data = S)
aes_key = HKDF-Expand(PRK_aes, "AirShield", 32)
hmac_key = HKDF-Expand(PRK_hmac, "AirShield", 32)
The two keys differ only in their salt. The info string is AirShield for both. For 32 bytes the expand step is one HMAC block with the counter byte 0x01.
The inputs cross between the two peers. The nonce is the receiver's challenge. The seed, IV and starting sequence come from the sender:
| Direction | Nonce | Seed | Initial IV | Starting sequence |
|---|---|---|---|---|
| Glasses to phone (receive) | the phone's RequestEncryption challenge | glasses EnableEncryption field 2 | glasses field 3 | glasses field 4 |
| Phone to glasses (send) | glasses RequestEncryption field 2 | the phone's seed | the phone's IV | the phone's random sequence |
The two directions share S and nothing else. Each has its own AES-256 key, HMAC-SHA-256 key, IV chain and sequence counter.
The channel binding is a 32-byte digest of the handshake values. The svc36 trust exchange signs this digest on every connect. The digest mixes the glasses' challenge and RequestEncryption key with the phone's seed and key:
binding = SHA-256( SHA-256(glasses challenge || glasses RequestEncryption key)
|| SHA-256(phone seed || phone public key) )
The phone signs the digest with NONEwithECDSA over the prehashed digest. Coordinates are fixed at 32 bytes, and s is in the low half. Any mistake shows up as 0x1043 INVALID_APP_IDENTITY. The message layout is on Identity-and-trust.
Every input is a public handshake value. The digest needs no private key, so anyone holding the handshake messages can recompute it. Each connect makes new ephemeral keys, so each connect makes a new digest and a new signature.
Two alternate inputs draw 0x1043 INVALID_APP_IDENTITY from the glasses. The phone sends only the primary digest.
Unconfirmed: whether the official app signs the same value.
The phone sends EndLinkSetup after trust. Its scope UUID and lease UUID must both be RFC 4122 version 4 UUIDs. The glasses check the two UUIDs with atc::UUID::valid(), which tests:
(b[6] & 0xf0) == 0x40(b[8] & 0xc0) == 0x80
The phone generates both UUIDs as version 4 values, so they pass both tests.
The rule matters because the glasses fail silently. A random 16-byte value passes both bit tests one time in 64, so two random UUIDs pass together about one time in 4096. With an invalid UUID, link setup completes, then every service answers service_not_found, and the channel is torn down. The failure looks like a permissions problem.
The phone sends EndLinkSetup only after the svc36 trust exchange. If it sends EndLinkSetup first, the glasses answer with a stream-0 LOST followed by silence, or with service_not_found on every service.
Unconfirmed: the glasses assign a scope to the peer at EndLinkSetup. The peer has no scope until trust gives it one. That would explain these symptoms.
The glasses send their own EndLinkSetup on channel 0x8001 after ours. The phone must answer with a DataX status frame on the mirror channel. The mirror clears the secure bit, so channel 0x8001 maps to 0x0001. The status carries error id 0x1000 with no payload:
80 04 00 01 03 00 10 00
That is 8 bytes of plaintext. It is padded to 16 bytes and sealed, so the frame on the wire is 26 bytes. The glasses emit a stream-0 LOST about 1 ms after an unanswered EndLinkSetup.
Link setup runs in five phases. Each phase depends on the one before it.
stateDiagram-v2
[*] --> Handshake
Handshake: RequestEncryption and EnableEncryption, both ways
Handshake --> Keys
Keys: ECDH and key schedule, nothing sent
Keys --> Trust
Trust: svc36 signed channel binding
Trust --> EndLinkSetup
EndLinkSetup: both sides send it, the phone answers with status 0x1000
EndLinkSetup --> Ready
Ready: sealed DataX traffic
The phone never sends EndLinkSetup before the trust exchange. A failed link setup leaves no keys. The next attempt makes new ones.
Times count from the first handshake message.
| Step | Time |
|---|---|
| Four handshake messages, both directions | 0 to 87 ms |
| Trust exchange | 91 to 103 ms |
| Phone's EndLinkSetup | 103 ms |
| Glasses' EndLinkSetup and the phone's answer | 104 to 106 ms |
| Glasses' stream-0 LOST after an unanswered EndLinkSetup | about 1 ms |
| Symptom | What the phone does | What the wire shows |
|---|---|---|
| The glasses send no handshake reply, or stop answering | The phone ends the attempt | The phone's two messages, then nothing |
| Peer key or IV has the wrong length | The phone ends the attempt | The glasses' two messages, then the phone closes the link |
| Trust verdict incomplete | The phone goes on to the ready phase | The glasses decide. Services answer service_not_found
|
| The glasses send no EndLinkSetup after the phone's | The phone ends the attempt | The phone's EndLinkSetup, then silence |
| UUID with a bad version or variant | The phone must not send it | The glasses tear down the channel silently, or answer service_not_found
|
| EndLinkSetup sent before trust | The phone sends trust first | Stream-0 LOST, then silence, or service_not_found on every service |
| Status answer to their EndLinkSetup missing | The phone must send it | The glasses emit stream-0 LOST about 1 ms later |
Reference code: SecureLink.kt, Messages.kt, Kdf.kt, Binding.kt
- Unknown: whether the glasses accept a field 7 value other than 17 in RequestEncryption.
- Unknown: how the glasses react to a peer public key that is not on the curve.
- Unknown: whether the glasses ever send fewer than two handshake messages.
- Unknown: whether the glasses' fields 7 and 8 ever change the outcome.
- Unknown: whether the two glasses public keys in the handshake are always equal on other units.
- Unknown: whether the status answer to the glasses' EndLinkSetup is the only answer they need.
- Unknown: why EndLinkSetup must follow trust. The section above gives the likely reason.
- Unknown: whether COMMAND frames can arrive during link setup over Bluetooth. See AirShield-frames-and-ciphers.
The glasses
The link
Identity
Media
Settings
Assistant