Building/Signing AUv2 for Gatekeeper w. Zip File

I have plugins building on my new Apple Silicon laptop, and I’m in the process of porting 248 individual plugins to AS. They are all very basic, generic interface AU v2s (I’m Chris from Airwindows: the work I do is MIT open source and entirely devoted to audio DSP code with minimal wrapping, in AU and VST)

I have access to the post on Signing A Mac Product For Distribution at https://developer.apple.com/forums/thread/128166

I have two preliminary builds for Acceleration and Acceleration2 up at https://github.com/airwindows/airwindows/tree/master/plugins/MacARMAU which have the project files I’ve settled on. They are set to ‘automatic’ signing though I’ve seen some inklings that the certificate I want is not Apple Development, but Developer ID Application. I have that DIA certificate in my keychain.

My target for distributing this is .component files contained in a zip file, and I’ll also accept a dedicated .dmg file if that’s required, though .zip would fit better with my other plugins such as Windows/Linux VSTs. I don’t know if it’s possible to code sign a generic AU, and I don’t know if my build settings are what they should be.

Basic idea is to continue to ship legacy Audio Units for intel Macs, not code signed, as needed… and to do the full Apple process, including code signing and notarization and stapling (I can work out what that’s about if I have some simple instructions, I don’t really understand the terminology) for the new Apple Silicon builds. It will be ‘don’t like using Terminal to run the old AUs on Catalina? Don’t use Catalina, or upgrade to Apple Silicon and Big Sur’ with the new AUv2 plugins passing Gatekeeper. Most of my users are comfortable with Terminal and already know to do this, but I don’t want it required for the new Apple Silicon builds.

So this is what I’ve got, and there’s about 246 more that I’m working through, one by one.

https://github.com/airwindows/airwindows/tree/master/plugins/MacARMAU

I expect to be changing some things and starting over from the beginning. Which things must be changed in the project configuration to build generic AUv2es properly (if anything), and what steps must I add to distribute these AUs via zip files that will clear Gatekeeper? I won’t be using installers and my users will be able to find and work with their Components folders, by default. Will zip files work, must I use .dmg to sign stuff inside the .dmg, can I put such a .dmg (if needed) inside a zip file for download?

They are set to ‘automatic’ signing though I’ve seen some inklings
that the certificate I want is not Apple Development, but Developer ID
Application.

I recommend that you leave your project set to automatic signing with Apple Development. Then, when it comes to distribution, you should follow the distribution workflow, that is:
  1. Create an Xcode archive (Product > Archive or the archive action in xcodebuild)

  2. Export that archive with the right signing for your distribution mechanism

This is very easy when shipping an app but less so with things that aren’t apps. I wrote this up for a different developer yesterday. Please read that through and then post back here with any remaining questions.

Basic idea is to continue to ship legacy Audio Units for intel Macs,
not code signed

Please sign and notarise all the code you ship, regardless of platform. While Apple silicon Macs won’t run unsigned code at all, code that’s not notarised may be blocked by Gatekeeper on any Mac running 10.15 or later.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Here is more detail on how it's going:

https://github.com/airwindows/airwindows/blob/master/plugins/AirwindowsAUToSignedAUProcess.txt

Quick precis: everything is signing, all the certificates are there, many hours of individually building and exporting built projects specifically through XCode's archive. The DropDMG format is zlib, no encryption, no license or layout, signing is Developer ID Application. It runs without problems. When I send the DMGs through SD Notary, telling it 'when submitting, create and staple disk image' using the same config from DropDMG (same result if I don't tell it to do that, though), it does:

The binary is not signed with a valid Developer ID certificate.
The signature does not include a secure timestamp.

It's been a solid week of building these Audio Units, and it's hard to tell whether I need to go through and alter more things in the XCode config and build every single one, again. Some of the packaging can be automated or scripted in Terminal, but the handling of XCode kind of can't, so it's RSI time as I do the 248 individual project files.

New name for the github section it's in, is https://github.com/airwindows/airwindows/tree/master/plugins/MacSignedAU since they are now also including Intel code, just to try and help people who are running into these kinds of headaches, and to provide an Intel build for people who have begun using Catalina or Big Sur on older machines.

I'm gonna ask again, not for a way to skip this hell, but for some pointers on what is still wrong with my project files as provided on GitHub. I'm going with .dmg distribution, using the third party apps DropDMG and SD Notary to streamline this process, and I am still nowhere, it would appear.

It looks like the relevant part of your advice is "For your command-line tool targets, use step 1 of the workflow but replace step 2 with your own script that exports and re-signs the tool from the archive. Finally, use your existing script to combine everything up into an installer package and then sign and notarise that package."

As you can see I can and do put scripts together for Terminal and use them, but I have no idea what you mean by 'exports and re-signs the tool from the archive'. I've got a whole bunch of exported .component files signed with XCode 'automatic' and I can verify they're code signed, I've got matching DMGs as well, but the process is failing somewhere. Why would they be signed if they're not because there isn't a secure timestamp? Is there a setting I must do for all 248 projects that'll enable this timestamp and cause it all to work?
To get a feel for how this should work I recommend that you work through the standard Xcode workflow:
  1. Create a simple test app using one of the built-in templates.

  2. Configure it to use automatic code signing with your Team ID.

  3. Build and run it locally, just to make sure it works.

  4. Do a Product > Archive.

  5. In the Organizer, click Distribute App and follow the Developer ID > Upload workflow.

Can you get that to work?

Note For a more detailed description of this process, see Notarizing macOS Software Before Distribution.



Once you have the above working, try extending it so that you notarise a disk image from the command line:
  1. Do steps 1 through 4 as above.

  2. In the Organizer, click Distribute App and follow the Developer ID > Export workflow. Xcode will create a Developer ID signed version of your app that’s ready to be notarised.

  3. Wrap the exported app in a disk image.

  4. Notarise that from the command line.

Can you get that to work?

Note For a more detailed description of this process, see Customizing the Notarization Workflow, although skip the Export a Package for Notarization section because that’s what step 2 is doing for you.



Once you have the basic workflow up and running for an app, we can talk about how to translate this to a non-app product, like your audio components.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Forgot to explicitly say: done that, yes the test app builds and signs and notarizes and staples perfectly. It's more or less configured to work right out of the box and gave me no trouble at all.

I do see one immediate difference in the test app, and all of the Audio Units (they all match: I've gone to some trouble to keep things consistent)

In Targets, Build Settings: Signing- Code Signing Entitlements the app comes with "macOS/macOS.entitlements" and all of the Audio Units are blank there, being translated from an earlier XCode without code signing. Though the project tried to update itself to newer and more appropriate settings, that remained blank. Everything else is identical, and the test app seems fine other than it doesn't have an App Category: easily fixed.

I would prefer to walk through the process using the third party tools which appear to be set up right and are doing the same thing as notarizing from the command line: I've got a 'The submission has been notarized and will now be stapled', and 'Stapling succeeded'. I could also do this from the command line but I think the result would be the same: works fine.

If I simply tell Build Settings (in the AU project) that Code Signing Entitlements is 'macOS/macOS.entitlements' it says it's missing the file. Removing the reference, gets me a working build again. What now?
Confirmed by two emails that both the Developer ID > Upload workflow and the Developer ID > Export workflow in a disk image and notarized separately, work without any trouble. :)

For the test app: not any of the Audio Unit plugins.

For the test app: not any of the Audio Unit plugins.

OK, cool.

In Targets, Build Settings: Signing- Code Signing Entitlements the app
comes with macOS/macOS.entitlements and all of the Audio Units are
blank

That’s expected. Entitlements are only applicable to a main executable. An audio unit is not a main executable (it’s a plug-in that’s loaded into some process that itself is running a main executable) and thus it never needs any entitlements.

So, if you got the disk image process working for a test app (as per “notarise a disk image from the command line”, above) then it’s a relatively short hop from there to getting an audio unit working. The sticking point is step 2:

In the Organizer, click Distribute App and follow the Developer ID >
Export workflow. Xcode will create a Developer ID signed version of
your app that’s ready to be notarised.

The Organiser is only able to work with Xcode archives that contain an app. If you have an Xcode archive containing an audio unit, you’ll see that the Organizer doesn’t show a Distribute App button but rather a Distribute Content button. So, you have to replace that step with something custom.

Consider this:

Note I don’t know anything about audio units but based on the GitHub links you posted above it looks like they’re all based on a bundle project so that’s why I’m using that in this example.
  1. Create a new (New > Project) from the macOS > Bundle template.

  2. In Signing & Capabilities, enable “Automatically manage signing” and select your team.

  3. In the Build Settings for the bundle target disable Skip Install (SKIP_INSTALL).

  4. Create a dummy class (using New > File > Cocoa Class). This is only needed so that the bundle has some code. Without it, you get a codeless bundle which is valid in general but not a good example here.

  5. Choose Product > Archive.

  6. In the Organizer, note that the button says Distribute Content not Distribute App.

  7. Control click the archive and choose Show in Finder.

If you rummage around in the the Xcode archive you’ll see it has a structure like this:

Code Block
PPP DDD.xcarchive/
Info.plist
Products/
Library/
Bundles/
PPP.bundle
SCMBlueprint
dSYMs/
PPP.bundle.dSYM


In this context PPP is the target name (which for a simple project matches the project name) and DDD is a timestamp that gives the archive a unique name.

Your next goal is to export the bundle from the archive and then sign it for distribution. This is actually pretty straightforward. To start, make a copy of the bundle:

Code Block
% cp -R "PPP DDD.xcarchive/Products/Library/Bundles/PPP.bundle" .


If you dump its code signature you’ll see its Development signed:

Code Block
% codesign -d -vvv "PPP DDD.xcarchive/Products/Library/Bundles/PPP.bundle"
Authority=Apple Development: Quinn Quinn (7XFU7D52S4)


You need to replace that with Developer ID signing:

Code Block
% codesign -s "Developer ID Application: TTT" -f --timestamp PPP.bundle
PPP.bundle: replacing existing signature


Note In the above line TTT identifies you team. For example, for my individual team this expands to Developer ID Application: Quinn Quinn (SKMME9E2Y8). If you only work in one team, using just Developer ID Application is fine.

At this point you have a notarisation-ready bundle. You can build a disk image from that and then notarise the disk image, just like you’ve already done for your test app.

The only gotcha here comes with the re-sign step. The above assumes that your bundle has no nested code. If your bundle contains nested code — for example, if you embed a framework within your bundle — you’ll need to sign that separately. My general advice is that you sign each code item separately, from the inside out. For more specific advice, see Signing a Mac Product For Distribution.



Oh, one list thing. The process described above is quite manual but, given that you have a lot of projects, you’ll want to automate this:
  • Automate the Product > Archive step by running xcodebuild with the archive action.

  • The info above shows how to automate the export step.

  • Signing a Mac Product For Distribution has advice on automating the disk image creation step.

  • You do your final notarisation using altool, which is perfectly amenable to automation.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Woot! Works perfectly. This is exactly what I needed. The key point here is that, set to Automatic, the .component file is Development signed, and codesign must be used to replace the signature with Developer ID Application. At this point, I have a notarisation-ready Audio Unit, and can build a disk image and then notarise the disk image. For my Audio Units, there are no nested code frameworks, so it works without issue.

I've packaged up an Audio Unit in this way, downloaded it on another machine, and opened it and installed the plugin quite seamlessly with no popup windows objecting to the process: very old school! Like things used to work.

One question.

Given that I can select 'manual' code signing and specify Developer ID Application directly in XCode, and this does everything required without any post-processing (and that 'automatic' uses the wrong cert), is there a practical reason why my workflow should not select manual code signing and specify the right thing from the start? I've tested this and it works.

Given that I can select 'manual' code signing and specify Developer ID
Application directly in XCode, and this does everything required
without any post-processing … is there a practical reason why my
workflow should not select manual code signing and specify the right
thing from the start?

In general I discourage this approach, for two reasons:
  • Automatic code signing is just easier. If you ever work on anything other than an audio unit — or even with an audio unit for anything other than the Mac — you’ll probably need a provisioning profile or some capabilities that are gated via entitlements, and that’s much harder to set up if you use manual code signing.

  • Developer ID signing identities are precious, which means that you need to take good care of them (see this post). Any significant organisation has all their developers using Development signing and then has a publication step where a restricted set of folks, those authorised to ship code on the organisation’s behalf, re-signs the product for distribution.

While neither of these might apply to your current situation, IMO it’s best to start as you mean to go on.

Oh, and you already have a publication step that builds the disk image and notarises it, so updating that to re-sign isn’t a major commitment.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Developer ID signing identities are precious, which means that you need to take good care of them (see this post). Any significant organisation has all their developers using Development signing and then has a publication step where a restricted set of folks, those authorised to ship code on the organisation’s behalf, re-signs the product for distribution.

I think it's worth emphasizing this point, and thank you for it: it does make sense. Automatic code signing worked flawlessly for an application, even when I don't code in Swift and had no idea what I was doing: it produced a built product that was safe to distribute through the App Store. It didn't matter whether I was significant: what I was doing was parse-able by the security checks, regardless.

All this doesn't depend on my ability as a developer, or the developer's superior, to personally audit the code and check that it's not problematic. The system rightly rejects it if it's not up to snuff. I'm hesitant to endorse anything that tries to 'work around' this system, except that I'm truly not: the distinctions in workflow, for a one-person operation that will never be other than a one-person operation, come down to 'write a Terminal shell script that can iterate over hundreds of files running codesign -s "Developer ID Application" -f --timestamp PPP.bundle substituting the project name for PPP… something I could do one project at a time by simply typing it… and which would result in code signing, if I'm not mistaken, that does NOT identify me and my signing identity?

From

Note In the above line TTT identifies you team. For example, for my individual team this expands to Developer ID Application: Quinn Quinn (SKMME9E2Y8). If you only work in one team, using just Developer ID Application is fine.

it sounds a bit like you can execute a more generic code signing? That or, if you don't specify the team explicitly, it will always tie the signing to your active Developer ID signing identity: it will 'expand' it to the only possible identity it has on tap. That seems like a correct behavior.

Forgive me for belaboring this: my experience trying to work through this problem as an open source developer whose code is used by other projects, and treated as an 'on ramp' by totally naive developers who truly don't know what they're doing, puts me into a position where I MUST correctly understand the details of how all this works, and why. What I've seen out there is that this subject is treated as black art and largely something to 'work around', which I don't feel serves anybody's purposes well. Now that I've found a genuine authority on the subject, and apparently solved my own problem several different possible ways, there's a temptation on my part to be intimidated by language like 'any significant organization' and to back off, treating the overall system as hostile to me and mine: I have captured the secret invocations to bypass Gatekeeper, and must flee lest I anger Apple and have all my work revoked! :D

When in fact I am doing no such thing: I am, either way I choose to do it (even configuring XCode for 'basic V2 audio unit manual code signing' for that case only) working WITH the larger Gatekeeper system the way it is intended, and producing a workflow where I am sending plugin code, in shipping form, to be audited by Apple so that it can run on user machines without USER workarounds.

I've got a user who's shipped double-digit millions of records sold, who is not himself a programmer or anything like that, and when repeatedly trying to get his 'secret weapon plugin' working on his new M1 laptop running Big Sur, one of the things he knew to try was running Terminal and manually stripping quarantine bits from the downloads. Neither of us thought anything of it, beyond 'oh good, you're a power user'. But that's not a power user, that is somebody who is normalizing the execution of code that hasn't been checked for malware. It seems like the less of that behavior out in the wild, the better.

It sounds like it would be best for me to correctly parse how the overall Gatekeeper/codesign system works, so I can communicate the correct way to view it: not as an opaque extra layer of difficulty to turn away small indie devs and about which little is told or known, but as a way to establish a trusted third party (that would be Apple) which can guarantee with reasonable certainty that the stuff you run, which doesn't throw up objections or barriers, also doesn't contain nefarious code that will hurt you or others.

In sound engineering, we've got a discipline known as mastering. It's sound-processing the two-track audio after it's already been mixed, to match it to user environments better. But traditionally it serves another purpose: to let a third party, another set of ears, into the picture and get their 'take' on how the mix sounds. This is crucial: listening to your own mix over and over can lead you to a place where you come to accept things that are actually quite weird, and the mastering engineer isn't fooled by your expectations: if you have gone awry, they'll catch it.

Am I right in thinking the path toward doing the code signing is less important than doing, and normalizing, code signing so Apple's security systems can serve as that third party 'mastering engineer' to check the work of coders, whether or not they're working in large organizations or consider themselves to be small and insignificant?

It seems like making this as seamless as possible, is the most important part, and I play a role in that to the extent that I'm producing a library of open source code which can on-ramp naive developers. They will acquire my attitudes towards Apple code signing, and my difficulties in doing this has been in part because the body of developers already out there seems prone to getting mad at Gatekeeper and code signing when it's not easy for them. I wish to acquire, and propagate, a different attitude toward required code signing :)

That or, if you don't specify the team explicitly, it will always tie
the signing to your active Developer ID signing identity: it will
'expand' it to the only possible identity it has on tap.

Kinda. codesign does a substring match to find the signing identity. If it finds exactly one, it uses that. If if finds none, that’s an obvious error. If it finds more than one, it complains about the ambiguity and stops. See the Signing Identities section of the codesign man page for the gory details.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Building/Signing AUv2 for Gatekeeper w. Zip File
 
 
Q