Skip to content
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 to Apple TV With TVOS 10.2 is broken #377

Closed
anjo03 opened this issue Apr 14, 2017 · 45 comments
Closed

Airplay to Apple TV With TVOS 10.2 is broken #377

anjo03 opened this issue Apr 14, 2017 · 45 comments
Labels

Comments

@anjo03
Copy link

anjo03 commented Apr 14, 2017

TVOS 10.2 requires Device Verification, which cannot be turned off anymore.
Since forked-daapd does not support Device Verification I am not able to stream music to my Apple TV anymore. I can see that Airparrot has found a work-around. Is it possible for you to update forked-daapd to reenable this feature again? Thanks in advance

@ejurgensen
Copy link
Member

Thanks for the heads up. I will look into it, but I am not very optimistic about being able to solve it. Looks like it might be a hard nut to crack. I also don't have a ATV 4th gen, though perhaps testing on a 3rd gen with Device Verification enabled is good enough.

I will add a few warnings to the docs about upgrading to tvOS 10.2.

@ejurgensen ejurgensen added the bug label Apr 14, 2017
@jompjomp
Copy link

jompjomp commented Apr 25, 2017

I'm not very happy with Apple right now. My Apple TV auto updated and by the time I realized daapd doesn't work with 10.2, they've apparently stopped signing signing 10.1.1, so I can't downgrade. Unbeliebly annoying to be unable to stream to my Apple TV for the foreseeable future.

@funtax
Copy link

funtax commented May 3, 2017

Maybe this project could get some help by Beamer, AirParrot or DoubleTwist? They have managed the verification-process.

@ejurgensen
Copy link
Member

Yes, might be worth a try. However, I'm still prevented from doing much about this as I don't have a new ATV. So even if I got the procedure spelled out, I couldn't implement it. If some ATV owner wants to take a crack at this it would be a blessing.

I am trying to borrow an ATV, though.

I'm also thinking that perhaps the verification is like the Remote pairing? It would certainly be lucky if it was.

@funtax
Copy link

funtax commented May 6, 2017

Today I realized that I can manually enforce the encryption on my ATV v3: This can also be used for the analysis/implementation!

I'm not sure if the procedure is equal to the Remote-pairing, but this might be possible.

@ejurgensen
Copy link
Member

ejurgensen commented May 25, 2017

I got my hands on a ATV3 to see if I could figure out how to do the device verification. So far no luck. It does seem to be based on SRP, so I made this little program for testing that implements the basic flow: https://github.com/ejurgensen/atv_verification (see pair.c)

It currently fails to verify at step 3. The proof which is calculated is apparently incorrect. The possible reasons for that are numerous - for instance I am not sure where exactly the username that is used is supposed to come from. I just copied the one I could see being sent. It is also an open question if SHA1 is the correct hashing algorithm to use.

I see that Airfoil has also now cracked the nut, and they also mentioned that no secret keys are needed. So it is clearly doable. I'm hoping the force of open source will show, and someone will take a stab at this.

@funtax
Copy link

funtax commented May 25, 2017 via email

@funtax
Copy link

funtax commented May 26, 2017

@ejurgensen Here's the link: https://github.com/funtax/AirPlayAuth
The SPR6a-part was really tricky - they base on SRP6a with some custom modifications.
Check the "/crypt/srp6/"-package inside my library to view the required modifications. Read the code and don't trust the method-descriptions there, they are not yet reflecting their method's body.

The username doesn't matter, I'm randomly generating it.. you just have to store your username with the eddsa-keypair for later authentication (that's why I'm simply putting them together into a string and split them later when used).

Step 3 is indeed the hardest one: Follow my code here step by step.

Hope it helps, congrats on your work!

@ejurgensen
Copy link
Member

Wow, the force of open source materialized, and it did so pretty quickly! Seems very impressive what you have done. I haven't looked at it yet, but will asap. I can't begin to imagine how you found about the custom modifications to SRP.

I'm not that strong with Java, so I might get back to you with some questions.

@funtax
Copy link

funtax commented May 26, 2017 via email

@ejurgensen
Copy link
Member

ejurgensen commented Jun 1, 2017

I started working on this and thanks to your awesome work funtax I expect to fix this in forked-daapd within a few weeks. It seems I will need to add a dependency to something like libsodium to get EdDSA, so that is a pity.

Those Apple customizations are really weird. I especially wonder what the point of this one is:
for (; lengthB >= 0 && 256 == ++aesIV[lengthA]; lengthA = lengthB += -1) ;

In most cases it just increases the rightmost byte by one, except if it wraps around in which case it proceeds left? And btw, why lengthA and B - aren't they always equal?

@funtax
Copy link

funtax commented Jun 1, 2017

Hey @ejurgensen 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.
I would have to check this in a quiet minute in the debugger if you don't "solve" it before :)

On the first view, yes, one variable seems to be obsolete.
It's not always just increasing the rightmost one but sometimes a few more positions from the right.

For the edDSA-stuff, search for "ed25519 supercop" which is the essence of this library.
For C there exists this one https://github.com/orlp/ed25519 which seems to match the requirements 👍

@ViktoriiaKh
Copy link

Thank you so much! You did a really great job!

@funtax I’m trying to port your implementation in Java to Objective-C.
Here's the link to my test project: AirPlayAuth-ObjC
I managed to pass three pair setup steps (largely thanks to @ejurgensen modifications in the csrp source code).
But I failed to pass the second step of the pair verify process.
Maybe I made a mistake when generating or encrypting the public key at the pair setup step 3 and something went wrong.

For the edDSA-stuff I use ed25519, as you suggested.
To generate a public and a shared key I use curve25519_donna.
For encryption I use the ObjC implemenation of the AES/GCM AesGcm and the CCCryptor object from the CommonCrypto framework (for AES/CTR).

Please take a look at my code.
I would be very grateful if you would give me a little help.
Thank you.

@funtax
Copy link

funtax commented Jun 2, 2017

@ViktoriiaKh I love to see how this is growing, that's exactly what I hoped for.
On the first two looks on your code, everything seems to be totally well.
But on the third look I found a (the?) mistake:

In doPairVerify2WithData() line 482+483 the aesKeyStr + aesIVStr must be "Pair-Verify-AES-Key" and "Pair-Verify-AES-IV" instead of "..-Setup-.."" as in PairSetup3-step.

Good chance that it's working then.

Ps. I will remove Objective-C off my "languages I like to learn"-list ;-)

@ViktoriiaKh
Copy link

@funtax thank you very much!!!
I've made an inattentive mistake. Thank you for noticing.
Now it works!!!
You are the best!!! 💋

@funtax
Copy link

funtax commented Jun 2, 2017

@ViktoriiaKh I'm happy to read this 👍

@ViktoriiaKh & @ejurgensen 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
Copy link

funtax commented Jun 2, 2017

Could someone with the latest ATVv4 capture the TXT-records of the device (.raop.tcp.local)?
Eg. with the "Bonjour Browser"-app on Android?

I have no ATVv4 here and would like to use the version-part to determine if authorization is required.
Unofficial ATVs (EZCast, software-receivers etc.) would fail if they don't support the authentication.

Maybe @anjo03 ?

@ViktoriiaKh
Copy link

@funtax
Here is the TXT record associated with my ATVv4:

deviceid = <43383a36 393a4344 3a33383a 36413a30 43>;
features = <30783541 37464646 46372c30 78434445>;
flags = <30783263 34>;
model = <4170706c 65545635 2c33>;
pi = <62643135 38393861 2d616533 612d3432 36352d38 3235652d 37616134 32323765 30623066>;
pk = <38396665 33363436 64356632 36306330 38333632 64333638 30333235 32623363 31333533 64663761 30383366 63316235 63336666 39393766 38633163 39613039>;
psi = <30353634 37313646 2d314541 352d3439 41442d42 4638462d 32353039 32344144 44444143>;
pw = <31>;
srcvers = <3332302e 3230>;
vv = <32>;

To determine whether a device verification is enabled I use the following code (I have reverse-engineered one application with AirPlay support):

- (void)netService:(NSNetService *)sender didUpdateTXTRecordData:(NSData *)data {
    NSDictionary *info = [NSNetService dictionaryFromTXTRecordData:data];
        
    NSData *flags = [info objectForKey:@"flags"];
    if (flags) {
        NSString *strFlags = [[[NSString alloc] initWithData:flags encoding:NSUTF8StringEncoding] autorelease];
        NSScanner *scanner = [[[NSScanner alloc] initWithString:strFlags] autorelease];
        unsigned int flags = 0;
        [scanner scanHexInt:&flags];
        BOOL isDeviceVerificationEnabled = (0 < (flags & (1 << 9)));
        NSLog(@"isDeviceVerificationEnabled = %d", isDeviceVerificationEnabled);
    }
 
//...
}

@funtax
Copy link

funtax commented Jun 2, 2017

@ViktoriiaKh Awesome, would be super great if this is working. I darkly remember that the values haven't changed when I switched the protection on/off.. maybe it was buffering and I have to check this again.

@funtax
Copy link

funtax commented Jun 2, 2017

@ViktoriiaKh Can you double-check your solution somehow?
The values changes if I set "Password" as a protection, but does not change if I toggle the "device verification" inside the AirPlay-settings of my ATVv3:

"sf" for raop is the same as "flags" for "airplay":

With verification enabled:
txtRecord.getValue("sf") = {byte[5]@6125}
0 = 48
1 = 120
2 = 50
3 = 52
4 = 52

Without verification (disabled):
txtRecord.getValue("sf") = {byte[5]@5704}
0 = 48
1 = 120
2 = 50
3 = 52
4 = 52

@ViktoriiaKh
Copy link

@funtax I went to the country for the weekend and I do not have my ATV on hand at the moment. I'll be back in two days. I'll double-check everything and write you as soon as I can.

@funtax
Copy link

funtax commented Jun 3, 2017

@ViktoriiaKh No problem, thanks anyway :)
For my project I will now simply enable the pairing for all AppleTVs except v2+v3.
V3 is the one those emulators are using, so excluding them in the pairing should be enough for me.

Happy casting!

@ejurgensen
Copy link
Member

I'm a bit behind you guys, but now I also have my C version working and in a clean(ish) state. Once again kudos for some excellent work, @funtax. I also peeked at your ObjC version @ViktoriiaKh and that was also great guidance.

For the purpose of future inclusion in forked-daapd I brought it down to a single .h and .c file, where I included only the srp stuff required for the verification. I decided to go with libsodium, mainly because I didn't feel like including thousands of lines of crypto code in forked-daapd that I don't comprehend. I guess openssl is getting the required 25519 crypto stuff too, so then I can drop the libsodium dependency and just rely on openssl.

Now the next part is getting the verification flow built into forked-daapd, so I will also have to figure out when to enable pairing, like you discussed above. Right now my idea is to not use the TXT, but to do something like this: Do I know the device and have an auth key for it? Then verify with the device. If not, try OPTIONS, and if I get an access denied response, initiate pairing. Shouldn't that work?

I also haven't really tried actually streaming anything to the device yet - I wonder if some crypto will be required for that? I think I read somewhere about ChaCha being used.

@funtax
Copy link

funtax commented Jun 7, 2017

Hey @ejurgensen , I'm happy to read your update!
Yes, you don't have to use the TXT-records - just check if you already know the device requires authentication, otherwise check if the OPTIONS returns an access-denied.
The ATV will display an "iTunes xy is required for.." but this message will disappear directly if you start the pairing.

Once your have "pair-verified" your TCP-connection, you don't need to change anything else - just use that authenticated connection for the further communication.
In my example you will see that I simply use the connection to play a video at the end.

"pyATV" reworked my template into Python.. maybe this might help you or anyone else somehow:
postlund/pyatv#88

I'm looking forward to your work 👍

@ejurgensen
Copy link
Member

In my example you will see that I simply use the connection to play a video at the end.

Yes I noticed, but I will be streaming directly to the device, not just giving a link to external media. I am thinking that perhaps the whole point of Apple's change in 10.2 is protecting that link against eavesdropping? Maybe they need to guarantee to iTunes content providers that the entire chain is DRM'ed? Otherwise I don't really get the point of it all. But maybe there is none...

@funtax
Copy link

funtax commented Jun 7, 2017

Uhh, okay that's something I don't know.
My (Android-) apps are streaming audio via RTSP (airtunes) in realtime to the receiver and there was no further modification required.
But I had contact with some other devs who purchased a GO-library for the authentication.
In their case, the further communication was indeed protected so they had to de- /encrypt the content-length and the content itself transmitted between the receivers.
But I'm not sure if that library did just more than essentially required like in our cases.

I didn't had to implement any new for the realtime audio-transmission (as done as in iTunes), so I'm thinking positive that it's also not required in your case :)

@funtax
Copy link

funtax commented Jun 7, 2017

Hah, I didn't ever read what this project is meant for.
Yes @ejurgensen, for raop you just have to use the authenticated connection for SETUP, ANNOUNCE etc... I'm doing the same in my apps :D

@ejurgensen
Copy link
Member

Cool, that's a great relief!

@ViktoriiaKh
Copy link

@funtax
My sincere apologies for delay in replying to you. It took me a little more time to double-check my solution.

I’ve examined the TXT-records associated with the AirPlay and RAOP services on two Apple TVs (2nd and 4th generation), and the check whether the device verification is enabled works for me.
The reason that it does not work for you remains a mystery to me.
But I am glad you’ve found the alternative solution.

Here are the results for AppleTV2,1:

_airplay._tcp

With verification enabled:

    deviceid = "B8:17:C2:DB:B8:B9";
    features = "0x4A7FFFF7,0xE";
    flags = 0x2c4;
    model = "AppleTV2,1";
    pk = <35323539 61656233 64633661 63633461 34356439 39353136 63303066 38653163 33643536 31663866 63663635 66616533 62633565 65316436 65313331 38643064>;
    pw = 1;
    srcvers = "200.54";
    vv = 2;

With verification disabled:

    deviceid = "B8:17:C2:DB:B8:B9";
    features = "0x4A7FFFF7,0xE";
    flags = 0xc4;
    model = "AppleTV2,1";
    pk = <35323539 61656233 64633661 63633461 34356439 39353136 63303066 38653163 33643536 31663866 63663635 66616533 62633565 65316436 65313331 38643064>;
    pw = 1;
    srcvers = "200.54";
    vv = 2;

_raop._tcp

With verification enabled:

    am = "AppleTV2,1";
    cn = "0,1,2,3";
    da = true;
    et = "0,3,5";
    ft = "0x4A7FFFF7,0xE";
    md = "0,1,2";
    pk = <35323539 61656233 64633661 63633461 34356439 39353136 63303066 38653163 33643536 31663866 63663635 66616533 62633565 65316436 65313331 38643064>;
    pw = true;
    sf = 0x2c4;
    tp = UDP;
    vn = 65537;
    vs = "200.54";
    vv = 2;

With verification disabled:

    am = "AppleTV2,1";
    cn = "0,1,2,3";
    da = true;
    et = "0,3,5";
    ft = "0x4A7FFFF7,0xE";
    md = "0,1,2";
    pk = <35323539 61656233 64633661 63633461 34356439 39353136 63303066 38653163 33643536 31663866 63663635 66616533 62633565 65316436 65313331 38643064>;
    pw = true;
    sf = 0xc4;
    tp = UDP;
    vn = 65537;
    vs = "200.54";
    vv = 2;

@ejurgensen
Copy link
Member

ejurgensen commented Jun 8, 2017

My ATV3 (_raop._tcp):

  • verification enabled: "sf=0x244"
  • verification disabled (and avahi restarted): "sf=0x244"
  • verification disabled, device restarted (and avahi restarted): "sf=0x44"

Now I will try enabling, then disabling and waiting a while (and with no restarts).

@funtax
Copy link

funtax commented Jun 8, 2017

Thank you @ViktoriiaKh & @ejurgensen, that's really interesting!
I had also no "delay" between toggling those features - looking forward to @ejurgensen 's results with the delay.

But for my case it's done via the simple am-matching :)

@ViktoriiaKh
Copy link

Hello everyone!
I have installed tvOS 11 beta 1 on my Apple TV and AirPlay does not work any more.
Here are the TXT records associated with the AirPlay and RAOP services on my Apple TV:
airplay
raop
Changes that Apple made in tvOS 11 beta 1 also broke AirParrot 2 and Beamer's ability to stream video to Apple TV.
Play request fails with 400 Bad Request Error.

I used Wireshark to capture and inspect packets that iTunes sends to Apple TV when connecting.
Here are the results obtained:

POST /fp-setup HTTP/1.1
User-Agent: iTunes/12.5.4 (Macintosh; OS X 10.12.2)
Content-Length: 16
Content-Type: application/octet-stream
X-Apple-Device-ID: 0x0C4DE9B8169C
 
FPLY............
 
HTTP/1.1 200 OK
Date: Fri, 16 Jun 2017 14:45:28 GMT
Content-Length: 142
Content-Type: application/octet-stream
Server: AirTunes/350.92.4
 
FPLY..............D^.	..F..1.s.".x.y+.|......3....;B..I....9B\.s4....cN..	.e./...........W..G5.Q..0.
> .....M..H...z.p..jy9....{...r./5FO4F…
 
 
POST /fp-setup HTTP/1.1
User-Agent: iTunes/12.5.4 (Macintosh; OS X 10.12.2)
Content-Length: 164
Content-Type: application/octet-stream
X-Apple-Device-ID: 0x0C4DE9B8169C
 
FPLY............u.Y.........ZK.j{.>.W...S....G.'&...b.*...v....Q .}....6;...?8?L?...]q.R..ESeVae.;Zr.8.-.e6..H..M.i.=@3>O..f....%.._.3....;;.w....yJ.(.......S6.....
 
HTTP/1.1 200 OK
Date: Fri, 16 Jun 2017 14:45:28 GMT
Content-Length: 32
Content-Type: application/octet-stream
Server: AirTunes/350.92.4
 
FPLY..........yJ.(.......S6.....
 
POST /fp-setup2 HTTP/1.1
User-Agent: iTunes/12.5.4 (Macintosh; OS X 10.12.2)
Content-Length: 120
Content-Type: application/octet-stream
X-Apple-Device-ID: 0x0C4DE9B8169C
 
FPLY.......l........{=l.dR./..D....@.|.l.F..(b..~.....,...k.x....1Gn..y]...VdJ...Z7.................................j
rt
 
HTTP/1.1 200 OK
Content-Length: 0
Server: AirTunes/350.92.4

FairPlay handshake seems to become mandatory.
Has anyone managed to handle this challenge?
Thanks in advance!

@funtax
Copy link

funtax commented Jun 17, 2017

Oh no, another cat 'n mice challenge :(
This is something I found on my first search about it:
https://reverseengineering.stackexchange.com/questions/6678/airplay-mirroring-decryption-fairplay

Seems like the receivers that support Fairplay, re-use Apple's binaries (sometimes via remote-access) to do the handshake.

I hope we can also get this challenge done.
I have no knowledge about how to break encryption, just in reading code written in other languages without a compiler between :D

@ejurgensen
Copy link
Member

Really bad news, especially now that the other challenge was just solved. Apple sure is doing its best at promoting Chromecast.

I thought Fairplay relied on Apple ID? Perhaps that is not so? If it does, then how does it work if neither the sender nor the receiver is logged in via Apple ID?

@ViktoriiaKh
Copy link

Hello, guys!
I'm sorry I misled you. Fortunately, FairPlay support is not mandatory in tvOS 11.
I've finally found what was wrong.
The play request failed with 400 Bad Request Error because the request body must be of type application/x-apple-binary-plist (text parameters are not supported any more).

POST /play HTTP/1.1
Content-Length: 253
X-Apple-Device-ID: 0x0C4DE9B8169C
Content-Type: application/x-apple-binary-plist
User-Agent: iTunes/12.5.4 (Macintosh; OS X 10.12.2)

The curious thing is that even device verification is not required.
Everything works fine.

ejurgensen added a commit that referenced this issue Jun 19, 2017
… 10.2 (fix for issue #377)

- also change how speakers are saved/retrieved from the db
- add generic authorization methods in outputs.c and player.c
- let filescanner read *.verification files (containing PIN)
- configure options to enable and disable, since libsodium is required
@ejurgensen
Copy link
Member

Good, that's really a relief. Strange if they are abandoning verification again - perhaps poor user feedback on 10.2?

Do you have an idea how iTunes determines if should use text/parameters or application/x-apple-binary-plist for SET_PARAMETER requests? That is, if it ever uses the latter - I only have a ATV3 to test with, and iTunes is using text/parameters towards it.

@ejurgensen
Copy link
Member

The issue should now be fixed, so that forked-daapd now supports device verification (see README for instructions). This is with a lot of thanks to the work of @funtax.

@anjo03
Copy link
Author

anjo03 commented Jun 24, 2017

Hey all. First of all: Thanks for all your hard work and effort of making this work!!! Maybe my setup is wrong somehow, but the initial verification works flawlessly, but after a restart/crash of the forked-daapd daemon, I need to re-verify the Apple TV again and again. Any idea why?

@ejurgensen
Copy link
Member

Yes, forked-daapd saves speaker settings (incl auth keys) when it exits. I didn't change that here, but I probably should. The current behavior means settings are lost if forked-daapd crashes. Of course, it shouldn't be crashing, so please open an issue on that (with steps to reproduce if you can).

Little workaround: If you verify and restart before the crash then the key will be saved, and you won't need to verify again.

@anjo03
Copy link
Author

anjo03 commented Jun 24, 2017 via email

@ejurgensen
Copy link
Member

Closing, should be fixed with 44fad55

@TribuneX
Copy link

TribuneX commented Jun 30, 2017

I am unable to pair with an ATV4. My log says:

[2017-06-30 22:54:20] [ LOG] raop: Verification step 4 to '$Name' failed with error code 511: Network Authentication Required
[2017-06-30 22:54:21] [ LOG] dacp: Speakers de/activation failed!

I included the password in the config file and set the AirPlay authentication to password.

While trying to install the latest version from github, I also got some errors during
sudo make install

libtool: install: /usr/bin/install -c forked-daapd /usr/sbin/forked-daapd
make[4]: Nothing to be done for 'install-data-am'.
make[4]: Leaving directory '/opt/forked-daapd/src'
make[3]: Leaving directory '/opt/forked-daapd/src'
make[2]: Leaving directory '/opt/forked-daapd/src'
make[2]: Entering directory '/opt/forked-daapd'
make[2]: *** No rule to make target 'scripts/freebsd_install_10.1.sh', needed by 'all-am'. Stop.
make[2]: Leaving directory '/opt/forked-daapd'
Makefile:612: recipe for target 'install-recursive' failed
make[1]: *** [install-recursive] Error 1
make[1]: Leaving directory '/opt/forked-daapd'
Makefile:909: recipe for target 'install' failed
make: *** [install] Error 2

Or does this only apply for the freebsd script, which is not necessary on my debian system anyway?

@ejurgensen
Copy link
Member

For pairing with an ATV4 you should not put a password in the config file. Please check the instructions here: https://github.com/ejurgensen/forked-daapd#airplay-devicesspeakers

Thanks for notifying about the install error. It should be fixed now.

@TribuneX
Copy link

It wasn't clear that I have to use the PIN method and that the password version does not work for tvOS 10.2 anymore. I got it working know using the PIN / Code setup. Thanks for the support!

@marcklefter
Copy link

For those interested, pairing with Apple TV implemented in nodejs: https://github.com/marcklefter/node-appletv-pairing.git

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants