-
-
Notifications
You must be signed in to change notification settings - Fork 101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AirPlay broken for tvOS 10.2+ #79
Comments
Maybe this project could get some help by Beamer, AirParrot or DoubleTwist? |
any updates here? :) |
Hey there, feel free to use my Java-library as a template: https://github.com/funtax/AirPlayAuth You can simply import the project eg. into "IntelliJ Community Edition" and run the example :) I'm the developer of two audio AirPlay-apps and can confirm that once the pairing has been done, the ongoing communication with the AirPlay-receiver is done like before. Also, there seems to be no reliable way to check if pairing is required, beside checking the mdns-data for "appletv" and "pk". |
@Ronelius As @funtax mentioned, he has reversed engineered the process so it should be possible to implement. There are some quirks that I need to figure out regarding the SRP process, but I hope to be able to use pysrp for that. My biggest issue at the moment is time, but fixing this would be really great (even though I'm not affected by it yet)! |
@postlund Yes, the SRP-part was the hardest part: It's based on SRP6a in general, with some custom modifications which you can see inside my repository. Follow the code and not the comments on the methods.. I'm not sure if they really reflect the method-body right now. If you can't pass this step, you might compare both implementations. But the SRP-engine would required a little modification for this as SRP contains a random "secret" generator which generated different values in every run. So you'd have to disable the generator. I'm pressing thumbs, I have also done this by reverse-engineering another program so it's definitely possible. Ps. You can use any ATV by manually enabling "device verification" under its AirPlay-settings. |
@funtax Yeah, I can imagine 😄 I'm gonna try your library, add some logging and extract "reference" values for all the steps so I more easily can verify that I'm on the right track. Most key exchange schemes uses nounces to mitigate replay attacks, I assume that is what you mean with "secret"? Yeah, I found that too. It's good that it's available on the ATV3 as well. I have a question for you as well. In your implementation you randomize a "client id" as part of your auth token. Is there a reason for that? Based on the wireshark dumps I have taken in the past, the MAC-address of the interface used for AirPlay communication was used for this. Using the MAC-address would remove the need for saving an additional identifier. |
@postlund Yes, the random nounce is the "secret". If you have access to the MAC, just is this instead of the random ID. For my two apps, I have simply created one "authToken" and placed them in both applications for all users. |
@funtax Ok, great! Then I know 😄 Ah, that makes sense. I see that it potentially could be a problem, yes. Then I might use an approach similar to you as well! Thanks for the hints, I will probably give you a mention if I hit any problems along the way 👍 |
@postlund I'm looking forward to your implementation. Consider creating a "library" like mine which might be included in other py-apps or act as a template for other implementations.. but I'm sure you do ;-) |
@funtax It will be part of pyatv as a separate module once I'm done, so it will be possible to import it from pyatv in case that particular functionality is needed (and using this library is not an option) 😄 But it would be too excessive to create a new package and publish that to pypi for just this algorithm alone. |
@funtax Can you tell me what the purpose of this snippet in doPairSetupPin3 is? 😮
Comparing the value of a byte with 256 will never succeed since max value of a byte is 255. So what I can see, this loop only increase the value of the last byte in aesIV by one? |
Hey @postlund Sadly no, I'm sorry. I have reversed another (obfuscated!) program and this was the line I didn't understand on the first go. Leaving it out didn't work so I just copied it 1:1 into Java-code. |
@funtax That's OK, just wanted to check if you knew the purpose. Using a decompiler can leave strange code like that, so I'm not really that surprised. I also wanted to let you know that I've made quite some progress. Currently, I'm only focusing on the algorithms and doing prototyping. So it's far, far, far from usable in any way (I barely communicate with the device yet), I merely verify that passwords and checksums are generated correctly. Almost everything crypto related is finished, it's the last EDDSA verification left. So I'm starting to feel confident that I can pull this off, which is nice. |
@postlund Incredible, glad to hear that! Once you pass behind pair-setup-#3 you are good to go! In case you like to "chat" about anything related to this via e-mail, type the java-package-Webaddress into your browser and follow it until you reach the corresponding app on Google-Play and use the support-address there. I just don't want to publish mine here ;-) |
@funtax Yeah, it's really nice! I actually have working verification now (corresponding to authenticate()) and everything except for pair-setup3 seems to work as expected. I think I mixed up some identifiers and that's why it didn't work when I tried yesterday, so it probably works as intended. Algorithm-wise it should only be generation of the private key left. I have not found anything similar to how EDDSA in the i2p package for python, so I guess I will have to play around with what I have. But my final step now is to generate a new key with your library, extract the private part and the seed and verify that they work with my library. If that succeeds, I can finally start refactoring and implementing support in pyatv. It's a lot of work left when also including documentation and tests, but it will really nice when it's done 😄 Absolutely, if I need any one-on-one help then I'll certainly contact you. I don't think I will have time to work any further on this for a couple of days though, we'll have to see. |
Could someone with the latest ATVv4 capture the TXT-records of the device (.raop.tcp.local)? I have no ATVv4 here and would like to use the version-part to determine if authorization is required. |
@funtax Another alternative is (probably) to use dmap.loginrequired in the server-info interface. I assume that has the same value as the bonjour information and is available across all device. |
Awesome, many thanks for your help, @jeanregisser & @postlund ! @postlund Is this the interface under "/server-info"? Because this produces an "access denied (403)" on protected devices (and is used by other software to "check" if the protection is active. Retrieving this info via the TXT-record would be of course the most straight forward way. |
@funtax Oh, that's some details I didn't know. The best solution in my case would be to not have to rely on bonjour data. Mainly because this project is used in Home Assistant, where configuration usually done manually by the user. So it would be best if the platform there handled everything automatically. But then again, the support is for Apple TV and nothing else so I can implement it in such a way that only the Apple TV is supported. Other fun news is that I have successfully ported all parts to python now. Both pairing and authentication works as well as key generation. So it should be smooth sailing now on! 😄 |
@postlund Yearh I'm happy to read this :)) Happy casting! |
So what does this mean for the end user in terms of changes from current functionality? :) |
@Ronelius Feature-wise nothing really changes, it brings back AirPlay streaming functionality to all devices running tvOS 10.2 or later. In reality we can probably interpolate this to ATV4 and later (my bet is that ATV5 is shown at the WWDC keynote tonight). |
So, I have pushed the start of the support. It's available in PR #88. If someone has time, it would really nice to get some feedback if it works or not. Usage is quite simple, just to auth:
The generated credentials must then be passed as an argument, otherwise play_url will not work:
It's also possible to just verify if a given set of credentials are verified:
As I said, any feedback would be great! 😄 |
A bit rusty, do you know how to install that pr using npm? |
npm is for nodejs so that's not gonna work 😉 I would recommend that you pull the repo and use a new venv for this, so it does not collide with your working library. There's a script that does most of this, so basically this:
Then you can use git pull to keep updated. |
what do you know... hehe 👍 I don't really know if I'm doing something wrong. But I'm getting "Unknown command":
It looks indeed as I have got the latest code pulled:
|
Hmm, that's odd. Can you do "which atvremote" in the shell and verify that it picks the correct binary? |
|
Yeah, that's the system-wide installed version and not the one installed in the venv. Did the setup-script succeed when you ran it? Also, you can try doing source bin/activate again to ensure the venv is active. After running the script it is not automatically activated. |
@Ronelius Yeah, it's generally best to use one venv per application. But great to hear that it works! 😄 It will be a while until I can finish this and also extend Home Assistant with support. So, my suggestion is that you do that authentication manually (like you did) so you get valid credentials. Then you use the shell command component (https://home-assistant.io/components/shell_command/) to run atvremote from command line. Since you have to activate the venv it's probably best to create a small shell script that does the setup for you, like:
I hope that works! |
Thanks, will do :) |
Since 0.3.0 is released now, this can be considered fixed! 🎉 |
@funtax great work, I am porting it to android version :) , you mentioned app that you reverse engineered, can you name that app? |
Hey @ingsaurabh, it's "AirAudio" + "AirSpot". |
Oh @ingsaurabh you asked for the app/software I reverse-engineered and not the software it's now used for. I don't want to disclose this information here. |
@funtax not a problem :P , so your android implementation uses socket or url connection |
@ingsaurabh It's a socket-connection as normal URL/HTTP-connections won't do the job. |
@funtax Have you tested this approach when security is selected as password, as in my case ATV5,3 pairing don't works and when I try to play something it throws 403 error code can you confirm this? |
You are right @ingsaurabh , in my quick test with my app, device-verification AND passwort-protection seems not to work out-of-the-box. Maybe the HTTP-authentication has to be done before the actual pairing is made. |
@funtax I have checked HTTP based auth also before pairing but that too fails with same 403 error and no header field to extract auth token |
I have not verified, but could the password protection be this one: |
Yes @postlund that's the password-authentication and done via a simple Digest-authentication. My apps are now doing first pair-verify and then the Digest-authentication which won't work. I'm not sure ig @ingsaurabh really tried the digest-authentication before? |
Hi - First, thanks for the work you've done here. I'm implementing a version in Perl (nobody's perfect :-)). So far the step2 does not work. The Perl SRP package does : I also noticed that you overload getSessionsKeyHash as well and could see a difference. If S = (B - (k * ((g^x)%N) )) ^ (a + (u * x)) % N then |
I don't think the evidence calculation should make any difference compared to the one bundled in your library. It seems to match the specification and I did not overload it when using srptools (for python). Regarding K, you should use |
Hey @philippe44, please don't trust the description inside my code but only the content of the code. Every method implemented in my repository is actually used and the SRP6 won't work otherwise. Checkout @postlund 's solution, this should be a good start for your implementation :-) |
Thanks @postlund and @funtax. I still receive an error when I'm sending the proof, so I'm not sure what's missing. Just a silly question. The plist items (pk, salt, proof) ... shall they be encoded in a special way? By default the Perl Crypt::SRP package use everything in raw format (I've tried hex, b64 but did not help and it seems that all is raw anyway). Crypt::SRP by default handles everything in raw. I was wondering why you do hex convert of pk and salt before calculating the proof |
Everything in the plist-responses shall be in binary (raw) format and not converted to hex. I would highly recommend to log the various variables for a successful pairing either with @funtax library or mine, just so you have reference values. Then you input the same parameters in your code (e.g private key, PIN code) and pin-point when they differ. That's how I did and just trying to "get it right" the first time would never have worked. |
Thanks @funtax and @postlund. I'm good now. Authentication & Verification works in Perl. I've been pretty unlucky with 2 packages from Perl (SRP and Plist) that each head a nasty bug which derailed me for a long while. I know need to do a C version as well, at least for the verification steps I'm also documenting the different steps needed for exchange the and I'll make this available later. This is a pretty big list of crypto functions needed! |
Just a quick follow-up: it's all working now. I've created a small doc that describes the protocol, in a format similar to http://nto.github.io/AirPlay.html. You can find it here https://htmlpreview.github.io/?https://github.com/philippe44/RAOP-Player/blob/master/doc/auth_protocol.html |
@philippe44 , @postlund |
Can't you get that from the doc I've made? |
@philippe44 yes, sure. Even I am trying to make my server without pw (so we are starting from 10.2. RTSP session authentication) IPad didnt accept my answer. As I can see, I should respond "with a body containing 96 bytes of data." as I did, but dont know what is " remaining bytes (usually 64) are some data <atv_data>" so random bytes are not accessible. |
And also Iam receiving messageInfoDefaultHttpRequest(chunked: false) |
understood - at this point, unfortunately I don't know. It might be that <atv_data> is a signature of something and iPad verifies it, but when writing a client, I did not have to verify it. Only thing I can recommend is to verify that all your other steps are correct using the test vector I've included. There is such an amount of crypto which include many options that any small mistep makes it wrong. Another thing: I'm not sure how you can start at 10.2 as what is done here requires that all other steps are done before. They must be done only once, but they must because the client will send you a request that relies on the fact that its pairing was accepted before |
@philippe44 @LionisIAm I had a look at the code I wrote for MRP, which also uses SRP but with a proecdure more or less looking like the one used by HomeKit. The <atv_data> parts seems to be a bit weird to me and my guess is that it's encrypted data with a signature of some sort (as you said, @philippe44). In the MRP code, the data corresponding to <atv_data> is decrypted with CHACHA20Poly1305 using the previously derived shared key. Basically like this:
The variable You can find the SRP code for MRP here (still WIP): https://github.com/postlund/pyatv/pull/114/files#diff-0db40656ea4dba91f392656886afa836R100 |
Has anyone figured out what <atv_data> contains? |
From tvOS 10.2 and onwards, device verification is now mandatory. This breaks AirPlay streaming for this version (and later). I will look into this as soon as I have some time.
https://www.google.se/amp/appleinsider.com/articles/17/03/29/tvos-102-update-requires-airplay-hardware-verification-breaks-third-party-streaming-apps/amp/
The text was updated successfully, but these errors were encountered: