ejurgensen / pair_ap

C implementation of Apple TV device verification (client) and Homekit pairing for AirPlay 2 (client + server)
MIT License
42 stars 8 forks source link

pair-fruit server implementation (now possible) #3

Closed fduncanh closed 10 months ago

fduncanh commented 10 months ago

@ejurgensen

Thank you for your work here. I am a maintainer of UxPlay, an AirPlay server (Legacy Pairing) open-source implementation.

Your test-code client-example.c in "fruit" mode was very useful in probing an AppleTV and getting a pair-fruit-type server implementation (addition of pair-setup-pin to an already-working pair-verify in UxPlay). FYI, here is a detailed writeup of the protocol

https://github.com/FDH2/UxPlay/wiki/crypto

Comments welcome (it could help in adding a pair-fruit server implementation to pair_ap)

Any Insight would be helpful, although you have presumably moved on to other things

ejurgensen commented 10 months ago

Thanks for the message, great to hear you could use my work. Also nice to learn about UxPlay and a pleasure to see another C Airplay solution. I maintain OwnTone, which as you might know acts as an Airplay client, so it would be interesting to check if the two work together.

Out of curiosity, what are the main differences between UxPlay and shairport-sync?

The write-up you made of the pairing process is excellent! I can't really add much to that, in fact it seems you have a better understanding on what is going on than I ever got. Due to my focus on OwnTone, I mostly focused on the client side. And yes, it's also been a while, so there are many things I don't recall, especially about the legacy/fruit pairing. One thing I stumbled on was this:

If the server's DSN_SD service announcement does not include pw=true, after the server has responded to the "Get /info" request, the client knows the server's deviceID and public key.

Maybe I don't understand it properly, but I don't think servers change announcement after the calls they receive? It also sounds weird to me if the server is making assumptions about what the client knows. As I recall, transient is simply a non-pin method of pairing, and the server's response to GET /info will show if it supports it (as will the announcement, but I also recall that it was generally better to use the GET /info response).

One think I am confused about in "fruit" mode, is that after all the work in pair-verify to set up a AES CTR 128 encrypted communication channel, UxPlay happily continues unencrypted client-server communication in plaintext. (only the audio and video media content is encrypted, but SPS and PPS NAL units, and all the earlier Setup stuff, are not ).

I'm not sure what SPS and PPS NAL units are, and again this is a bit hazy to me, but I think Airplay 1 in general focuses on just encrypting the payload? Maybe because the goal for Apple was to assure copyright holders that their content couldn't be wiretapped.

Is full encryption only a feature of HomeKit pairing?, or is some message from the server needed to switch to full encryption in "fruit mode" (Legacy Pairing)?

I don't know the answer to this, but if a switch exists I would think it would be in a message from the client?

Sorry, that I can't help you more!

ejurgensen commented 10 months ago

Btw, I see you use csrp, as does pair_ap. However, I remember finding a bug in the original csrp, so the versions in pair_ap have a fix. Unfortunately, I don't remember the exact bug, maybe it's not relevant to you. Maybe if you diff or check the git history of pair_fruit.c you can see what changes I have made to csrp.

fduncanh commented 10 months ago

Thanks for the feedback! (and your work on pair_ap)

UxPlay is mainly for video (screen mirroring), with audio-only mode as an "extra". SPS/PPS NAL units are video format things.

I did find a bug in SRP (not in the master branch, but in the 6a branch where extra big primes had been added by an earlier PR , and got it fixed with a PR that was accepted; I was quite confused by the bug until I recognized it )

I corrected my unclear text about transient pairing: This should read:

This mode is used by the client if the server's DSN_SD service announcement does not include pw=true. After the server has responded to the "Get /info" request, the client knows the server's deviceID and public key.

Maybe the Get/Info data also says it.?

ejurgensen commented 10 months ago

This mode is used by the client if the server's DSN_SD service announcement does not include pw=true

I think pw=true means that the client should use password-based auth? On my ATV4, I can configure a password in the settings. I don't have that set, so the announcement from my ATV4 doesn't include pw=true, but I believe PIN-based auth is still required. The server flags what type of auth is required in the status code, see this: https://openairplay.github.io/airplay-spec/status_flags.html

Maybe the Get/Info data also says it.?

Yes, both deviceID and public key are in the response. As are feature and status flags. The deviceID doesn't seem to be in the mdns announcement. I see my client implementation doesn't care about the deviceID, so I guess it's not used for anything other than identifying the server.

ejurgensen commented 10 months ago

I notice my ATV4 announcements 0x644 in the status flags, which should mean OneTimePairingRequired, but not PINRequired. So perhaps that means transient is supported? I see I never added transient to fruit mode, so can't test it easily.

ejurgensen commented 10 months ago

One more note: Transient in Homekit mode means the client uses a fixed PIN that is 3939. Not sure how what that should then be for fruit/legacy pairing.

fduncanh commented 10 months ago

I looked at what _airplay._tcp and _raop._tcp are saying with the iOS tool on an iPad (I don't know how to make avahi-browse give that info.)

the device ID is only in _airplay._tcp, where the TXT RECORD for UxPlay has deviceid, features, flags, model, pi, pk srcvers, vv

Apple TV gen 3 has osvers in addition to those

_raop._tcp (UxPlay) has

am, ch, cn, da, et, ft, md, pk, pw, rhd, sf, sr, ss, sv, tp, txtvers, vn, vs, vv

Apple TV gen 3 _raop._tcp doesnt have ch, rhd, sr, ss, sv, txtvers, and does have ov (=osvers)

(UxPlay announces ch = 2 channels, rhd = 5.6.0.0, sr = 44100 , ss=16, sv =false, txtvers=1)

In the mirror protocol (legacy) initial contact is with _airplay._tcp (which announces the deviceID), and then switches to raop. I guess if the audio-only airplay button in the iOS device is used, the initial contact will be with _raop._tcp where the deviceID is only known after GET/Info.

UxPlay has inherited its Airplay/Raop implementation from Shairplay->AirServer->RPiPlay->UxPlay (no connection to Shairport-sync in its history), so I'm not completely knowledgeable about all the details.

I was happy to find that UxPlay's "fruit" server pair-verify was complete when I added pair-setup-pin (it only had the transient pair-setup implemented, and recently someone had found that server pair-setup/pair-verify was not in fact necessary when features bit 27 was off, saving a 5 second lag in connections)