Monthly Archives: October 2014

Yosemite’s Dark Mode

If you’re a Mac developer you’re no doubt feeling that restlessness that comes when a major new OS release is nigh. Mac OS X 10.10 Yosemite has been cycling through “GM Candidate” releases for the past couple weeks, and many people seem to think it’s likely to be released to the public later this month.

One of the big user-facing visual changes is an optional “dark mode,” accessible from the General tab of System Preferences. When a user ticks this on, high level UI elements such as the menu bar and dock adopt a different style of drawing which you can roughly characterize as being white-on-black instead of the default black-on-white Mac appearance.

One hiccup for developers of third party apps is if you have an NSStatusItem or other custom menu bar item with a custom icon, you probably designed it so that it looks good on the white or light default menu bar.

Unfortunately, Apple has not provided a sanctioned method for detecting whether the user has switched to “dark mode,” so it’s non-trivial to tune your art to suit each of the modes at runtime. Instead, Apple recommends that all such icons should be designed such that Apple can effectively color them as appropriate for the current mode.

Luckily it’s pretty easy to get this behavior for custom status bar icons. You probably already call -[NSStatusItem setIcon:yourImage] with your custom image. Now, just make sure to call -[yourImage setTemplate:YES], and you’re done. The only caveat I would add here is that Mac OS X 10.9 and earlier do not seem to respect the “template” attribute when drawing status items, so I think the safest bet is to check for 10.10 at runtime, and omit the call to setTemplate if you’re running any earlier system:

BOOL oldBusted = (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9)
if (!oldBusted)
{
	// 10.10 or higher, so setTemplate: is safe
	[myIcon setTemplate:YES]
}

Now your status item icon looks great on 10.10, where in fact its coloring may even be slightly altered in light mode, and in 10.9, where it looks, well, like it always did. Update: thanks to Rick Fillion and Will Cosgrove for explaining that my problems on 10.9 are probably because my image is not designed right for use as a template. I’ll leave the solution to that as an exercise for the writer.

Unfortunately, this promise does not hold true for regular NSMenuItem items that are installed at the highest level in the menu bar. These are relatively uncommon, but for example you might find some apps with a gear icon, a script icon, or other similar graphic in lieu of a text-based menu title. When an icon is supplied to such a menu item, the template nature is not meaningfully respected in dark mode, so the icon draws more or less as-is, and is likely to almost disappear because of the lack of contrast. If you happen to be running 10.10 you can witness this problem by running iTunes and seeing that at least one script is in ~/Library/iTunes/Scripts. Its script menu icon appears right next to the “Help” menu.

I reported the issue to Apple and it came back as a duplicate. As of the latest 10.10 GM candidate, it has not been fixed, but I got a reassuring tweet from Apple’s own Eric Schlegel today:

So in summary, if you use icons as the “titles” of menus either inside or outside of the status bar area of the menu bar, be sure to call setTemplate:YES on it when running on 10.10. If you happen to have a menu bar icon affected by the bug I reported, I recommend creating a test project that installs an NSStatusItem with the same icon, so you can get a sense for how it’s going to look in dark mode when its template nature is suitably handled.

What To Do About Code Signing

I wrote about the confusion arising from Apple’s poor communication about new code signature requirements, and then just earlier today I wrote about the revelation that many apps have been reprieved by an Apple whitelist.

I tend to write long, so let’s cut to the chase: what do you need to do if you are a Mac developer who ships software directly to customers or via the Mac App Store?

  1. You must ensure your next software release is signed with a Version 2 code signature. The easiest way to do this is to build and sign your product on Mac OS X 10.9 or later.
  2. You must test your existing, published apps to see that they can be downloaded and opened without issue on 10.9.5 and 10.10. To be safe, try to test once from a machine/install that has never seen the app before. If you’re concerned, read the details in my previous posts to assure yourself that your existing app was whitelisted.
  3. If your existing app does not open or in particular if it receives an “obsolete resource envelope” assessment from the “spctl” tool, you must either release a new version of your app signed with a Version 2 code signature, or re-sign your existing app. Otherwise, people who download the app will be greeted by a warning that the software is “not trusted.”

That’s it, folks. Everybody has to start signing with the modern code-signing infrastructure. In the interim, there’s a good chance your app has been whitelisted to operate as usual during the transition, but that courtesy will probably not extend to your next release.

Gatekeeper’s Opaque Whitelist

I wrote previously about the confusion that arose when many developers, trying to comply with Apple’s new code signing rules, ran across strange system behavior in which version 1 signatures seemed to work, yielding the curious system policy message “accepted CDHash.”

I can’t believe I didn’t think until now to check Apple’s open source Security framework for clues about this. Poking around today I found something very curious. In the policyengine.cpp source file, search for “consult the whitelist” and you’ll find the clump of code that very deliberately avoids rejecting an app for its “obsolete resource envelope” if it passes some whitelist check:

if (mOpaqueWhitelist.contains(code, rc, trace))
	allow = true;

Well I’ll be darned. Could this explain the fact that many, many people observed that their apps with old, V1 signatures continue to pass Gatekeeper’s scrutiny e.g. on 10.9.5, even though Apple stated that V2 code signatures would be required?

The whitelist database is stored on your Mac in the following location:

/var/db/gkopaque.bundle/Contents/Resources/gkopaque.db

Go ahead, poke around at it. It’s big! It will be a bit easier to play with if you have any experience at all using the sqlite3 command line tool. With it, for example I was able to discover that there’s a table called “whitelist” within it, and that it contains two columns: “current” and “opaque”. And there are a LOT of rows in the table:

sqlite> select COUNT(*) from whitelist
68101

So what does a typical row from the table look like?

sqlite> select quote(current), quote(opaque) \
           from whitelist limit 1;
X'000327ECE1FB5A27B5F5C51A009900B1E4854BB7'|
X'CBE56B9784974E0A1C0159C41F392B77421B4D23'

By scrutinizing the code and poking around, I’ve determined that the “current” column is not unique, and corresponds to the “CDHash” of a given code object being analyzed. For example, a version of MarsEdit that shipped with a V1 code signature and which does not seem affected by the changes in Apple’s Gatekeeper policy has a CDHash of “D1FBA2AB9A4814877BE8C1D2A8615FB48D8D4026”, and on my system anyway there are two rows corresponding to that CDHash.

I don’t really get what the “opaque” column is about, and my ability to scrutinize Security source code isn’t great enough to easily be able to tell by reading the source, either. But it seems to me that it must somehow be a way of informing the security system that certain specific (possibly modified?) instances of an app are still essentially the same as the “current” CDHash being tested.

Using some more sqlite3 magic, we can determine the number of unique values in the “current” column:

sqlite> select count(distinct current) from whitelist;
36215

OK, I run a lot of software, but I’m quite positive I have not run 36K unique parcels of code in recent memory. My suspicion is that in the run-up to the major changes Apple has made to Gatekeeper, they painstakingly accumulated a list of 36215 “trusted” hashes and deposited them on everybody’s Mac so that the effect of 10.9.5’s stricter code signing checks would be mitigated.

One test I did to confirm that the database is not just a personalized list of the apps I have used, was to download an app that I don’t use regularly, Panic’s Unison, but that I thought would be popular and reputable enough to appear on Apple’s whitelist. I downloaded it, and before running it even once, I checked its CDHash (“35d9c847ebb7461aee5f08bb8e017b5a3891bc0f”) with the database. Sure enough, 7 rows match this CDHash. Perhaps accommodating 7 distinct releases of Unison? Again, I’m not sure. I then took the extra step of logging into another machine entirely, one which has never seen Unison even as a downloaded file. The database on that machine also contains the hash.

Want to see if your apps are “whitelisted” or not? It’s pretty easy to do from the Terminal:

  1. Find your CDHash by running “codesign -dvvv Your.app” and searching for the CDHash value in the output.
  2. Grep the database for your value:
    sqlite3 /var/db/gkopaque.bundle/Contents/Resources/gkopaque.db "select quote(current), quote(opaque) from whitelist" | grep -i [yourCDHashHere]
    

This whitelist offers a significant amount of explanation as to why some apps are allowed to launch without issue on 10.9.5 and 10.10. I don’t understand the database completely, particularly the meaning of those “opaque” CDHash values in the second column, but I feel as though a lot of mysterious behavior on all of our Macs is suddenly a lot more understandable.

Update later on Oct 6: After publishing this entry, Ed Marczak chimed in on Twitter with some information about the database:

In other words, according to Ed, Apple gathered the list of “whitelisted CDHashes” by surveying the software that people actually run, and reporting that data to Apple in the form the unique code signature hashes. I’m not sure what criteria they applied in the end to decide which of those apps are included in the whitelist, but it sounds reasonable to assume that if nobody ran your app on 10.9.4, Apple did not have the opportunity to consider including you in the whitelist.

(Thanks to Jeff Johnson for talking through some of the discoveries that led to this post).