Pairing
The pairing process uses the SRP protocol. The actual process is more or less the same as the one used by HomeKit, so the HomeKit API reference released by Apple is a good read when understading the process.
It uses CryptoPairingMessage
.
syntax = "proto2";
import "ProtocolMessage.proto";
extend ProtocolMessage {
optional CryptoPairingMessage cryptoPairingMessage = 39;
}
message CryptoPairingMessage {
required bytes pairingData = 1; // Example: <00010006 0101>
required int32 status = 2; // Example: 0
}
pairingData
contains TLV8 encoded data.
Differences Compared to HomeKit
When deriving new encryption keys, use the following HKDF-parameters:
- Salt: MediaRemote-Salt
- Data: MediaRemote-Write-Encryption-Key
When deriving new decryption keys, use the following HKDF-parameters:
- Salt: MediaRemote-Salt
- Data: MediaRemote-Read-Encryption-Key
Pairing with a New Apple TV
CLIENT -> SERVER
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: "\000\001\000\006\001\001"
status: 0
}
Decoded TLV
0: 0x00 // METHOD
6: 0x01 // SEQUENCE_NUM
SERVER -> CLIENT
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: "\006\001\002\002\0204_\017\223\261\266\223l\004x\nm\356Jlw\003\377\265rK\301\017\'\227\374\"yUG\321\253mB\213\233\226\212\353\003C\225\302}[qQ\350\376A\262\251\367@\356\246\037\304\305g\020\315\231Z\306-\377\016\200\324>\010f\t\'\305\200<m\334V\\\206\345#\276T\310\316\322\034\340C\215\265\326F\0344\321\225\010\354\342\276!\320\311\276\200G\021.\352O\357)\'\277I\205\272^s\0330\035\267U\377\233MpF\213\000\235\244\006\277\255\t\227\273\000VX\370f\341i\357,D\202\234\213D\377\247\217Y\266T\223\\\302\322T\352\377\ng|\315\357\354\341c\3267\366\277\0021v]w\364\247(\362aM\326\230-\277.\314=n:\275\004X\006q\025\332XZ/\271\273\333\334g\2523}l\006\276\031\322\002UD\252\252\3426\264\030\177\271\3160-2\346\214\352\262I\267\317\025x\322!\3416\267\\\314\010Z\204\317\260m\r\334\340\367\352\205\233\3509\224\003\201>\371]\027\200\266\235\330\212\256m\315\253W\234\'V\313\357\365D2\370\227(\272\014P\364\010\023b\016z\207\027\270\213\266G\314\nFU\226)\265=\345\257\246\274L\036u-\326u\326O\233`\303\254\026\304L\003I\223\247\\1`\243\231:C\221\005\\E\355\347\3079/}\033\237\353\357Sn\263)\237r8\020\260.\177\217|\246\260\036/\331\251\254\261\006\232\223\317\374y\030c\314>Y\217:\200\277\336"
status: 0
}
Decoded TLV
2: bytes[16] // SALT
3: bytes[384] // PUBLIC_KEY
6: 0x02 // SEQUENCE_NUM
At this point a 4 digits PIN is displayed on the Apple TV. The client uses it to derive the password proof in the next message.
CLIENT -> SERVER
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: "\006\001\003\003\377\327\200\354\213\314)\241\257\240V\357L\027\361\356\017\256\244!\016\r\364\'U\217C\nP[Hx@\221\316\225\353\311\2676Sw\235\034\374\347O\235\333\234ZS\037\214\372\3542\374\262r\244\003\345| \204\017\024\331\351\364j\361\340\237\2771\001^*V4\377\013\216\251\207\221\260\017\027\027\276\252x)\333\034\327rD\345\366\t\312\370%\256\237X\006\340]\271d\004\244\377\371;\256H\207\2537y[\364N#W|\367\034\240A5!\033B\336\227\203\001\203sf:<\365\272\263t\204P\022p\2045r\236u\035i+\316\242\004T\023\217\373\214\316\232j\315#\220@H\000o\t\304\365\263\230B\266\204ns\213\220\320\222\021I\016\334%\031\252y\206\347\236\370\0162\215/U\312ATb\324W=\253\267(\207\270\342\356\304\244\344%\030\035\002\376j\314\321\'O\227D\330Z\264g\016\315:\037\232\033\350-}\003\201\330\353eC\250\342\241\365\273\345\010k\356T\343\210)\307$\376\347]C\031]\177x\201\204\203\031\020A<\333\220\006\355t=\272\375|\033z]\3706\206Q\225\263\254\033PT\347\370t\352\215\351\215\320SR\374w\250\227U\004\317\320\210W\000\352Q\250\302\300\317Mu!\025\352\271}/\214\303\352\342\247gL\372\\Q\362W2\020\312X\321\n\253;?\310\366b-\226\205\230\031\303\027\370\223zv\235\006\017\004@\360\251\213\336\315?\n`9\315\01070?r\000\275\3236|\032\326\265\365\225\325R\320\\Q\246\227\344\216\374\t7._\375\005\224\213\253k\323\266\214\223\353^\204\337\211`+\215\334tF \352\232\211"
status: 0
}
Decoded TLV
3: bytes[384] // PUBLIC_KEY
4: bytes[64] // PASSWORD_PROOF
6: 0x03 // SEQUENCE_NUM
SERVER -> CLIENT
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: ??
status: 0
}
Decoded TLV
4: bytes[64] // PASSWORD_PROOF
6: 0x04 // SEQUENCE_NUM
CLIENT -> SERVER
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: ??
status: 0
}
Decoded TLV
5: bytes[154] // ENCRYPTED_DATA
6: 0x05 // SEQUENCE_NUM
SERVER -> CLIENT
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: ??
status: 0
}
Decoded TLV
5: bytes[154] // ENCRYPTED_DATA
6: 0x06 // SEQUENCE_NUM
Client then starts verifying the pairing.
Verifying Existing Pairing
CLIENT -> SERVER
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: ??
status: 0
}
Decoded TLV
3: bytes[32] // PUBLIC_KEY
6: 0x01 // SEQUENCE_NUM
SERVER -> CLIENT
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: ??
status: 0
}
Decoded TLV
3: bytes[32] // PUBLIC_KEY
5: bytes[120] // ENCRYPTED_DATA
6: 0x02 // SEQUENCE_NUM
CLIENT -> SERVER
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: ??
status: 0
}
Decoded TLV
5: bytes[120] // ENCRYPTED_DATA
6: 0x03 // SEQUENCE_NUM
SERVER -> CLIENT
type: CRYPTO_PAIRING_MESSAGE
priority: 0
[cryptoPairingMessage] {
pairingData: "\006\001\004"
status: 0
}
Decoded TLV
6: 0x04 // SEQUENCE_NUM
Once pairing is successful, all subsequent messages are encrypted using ChaCha20-Poly1305.
Outcome of Pairing
After pairing has succeeded each side (Apple TV and Remote app) will have the following data:
- Long-Term-Signing-Key (LTSK) - 32 byte
- Long-Term-Public-Key (LTPK) - 32 byte
- Apple TV Identifier - 36 byte
- iOS Identifier - 36 byte
The keys (LTSK and LTPK) are generated per-device, meaning they are different on each device. LTSK is never publically exchanged. These parameters must be persistently stored and are used to derive a "shared secret" (=encryption key) for new sessions without having to pair again. If they are lost, the pairing process must be performed again.