HinTak / Font-Validator

Font Validator is a tool for testing fonts prior to release. This testing ensures that fonts meet Microsoft's high quality standards and perform exceptionally well on Microsoft's platform.
Other
146 stars 12 forks source link

The FontValidator binary can’t be code signed on macOS #53

Closed schriftgestalt closed 4 years ago

schriftgestalt commented 4 years ago

To be able to update the macUI for MacOS 10.15, I need to code sign the binary. But I get errors as described here: https://stackoverflow.com/a/48336883/5329717

HinTak commented 4 years ago

Oh dear. That's indeed an issue - the binary is built by appending stuff to the end of itself, and the binary looks at itself beyond the executable part. I'll have a look around upstream (the mono people) to see if they have an answer.

HinTak commented 4 years ago

There is indeed an upstream bug : https://xamarin.github.io/bugzilla-archives/52/52443/bug.html

I'll see if I can find any more recent work to fix that.

HinTak commented 4 years ago

@schriftgestalt can you have a look at the binary under the codesign-fix branch ? If it works, I'll just pull it into master, and do the future binaries the same way. FontVal is kind of in need of an update anyway.

I have written a python script using macholib, based on the advice at the bottom of https://github.com/pyinstaller/pyinstaller/wiki/Recipe-OSX-Code-Signing , to work-around the upstream bug https://github.com/mono/mono/issues/17881 .

HinTak commented 4 years ago

@schriftgestalt I have pulled the codesign fix branch into master, and will be deleting that branch. Would appreciate if you give it a try sometimes.

threeseat commented 4 years ago

I was able to codesign the new binary

$ codesign --force --strict --sign 210065C8E28654BEEA636A5D030A09E72CEE178F -o runtime FontValidator
FontValidator: replacing existing signature`
FontValidator: signed Mach-O thin (x86_64) [FontValidator]
$ codesign --verify FontValidator
FontValidator: valid on disk
FontValidator: satisfies its Designated Requirement
schriftgestalt commented 4 years ago

It works now. I’m preparing a new version.

HinTak commented 4 years ago

@schriftgestalt. @threeseat : Good to know. Thanks a lot. Please give me a pull of your updates.

threeseat commented 4 years ago

@schriftgestalt : The binary included in the build and in the source tree is the Mono-dependent one, not the stand-alone executable.

@HinTak : Your signable binary was version 2.1.1. Do you plan to update it to a more recent release?

HinTak commented 4 years ago

@threeseat apologies, you are right. Fixed now. Actually I like to do a 2.1.5 soon, but it will be a bit (a few weeks at least?).

threeseat commented 4 years ago

I wasn't able to run version 7 of the Mac app either. When I try, I get a popup window with the error message Something went wrong: Unknown command line option: '-file'

When I try to directly run the FontValidator command-line executable, whether in the app bundle's Resources directory or in the MacUI/FontValidator folder, I get the error message Usage is: mono [options] program [program-options]

I misdiagnosed the problem with version 6 of the Mac app -- the trouble arises from codesigning the Mono executable:

I'm able to locally build and run the app with the unsigned command-line binary. That might be just because I'm building and running it locally. @schriftgestalt, Can a working app be distributed without codesigning the binary?

HinTak commented 4 years ago

Oh dear. I 'll have a look what is broken. Likely an upstream issue.

threeseat commented 4 years ago

It appears related to mono/mono#18826

HinTak commented 4 years ago

@threeseat thanks for looking it up. I thought it might be something like that - I 'll see if I can fix it somehow.

HinTak commented 4 years ago

I have looked at the actual code, it is not hard, in principle, to fix - mono looks a the end of itself to find the special token, the signature block appends, so things break. It mostly needs to have another go to find the special token before the signature. I am looking at how variable it is - if the worse come to the worse, you can always scan barbarically backwards, but I'd rather not do that. So this means writing a small header parser to fins the special token, xmonkeyslovemagic.

HinTak commented 4 years ago

I posted a patch to https://github.com/mono/mono/issues/18826 - I'll need to build mono itself (at least the loader part) for testing.

HinTak commented 4 years ago

@schriftgestalt @threeseat It has taken a while - building a patched mono, down a rabbit hole of some compiler issues - anyway, please try to sign the top of the master branch a try again, and see if that works now, and let me know either way, Btw, this needs os x 10.13 onwards - I hope you (and your typical users...) are up to date enough.

threeseat commented 4 years ago

I was able to codesign the binary at MacUI/FontValidator/FontValidator/FontValidator, but because the included FreeType dylib is unsigned, I had to disable library validation for the hardened runtime.

I've created a pull request (#61) that has the library codesigned with an ad hoc signature, in case that resolves the issue.

HinTak commented 4 years ago

Thanks for doing this. Oh, that's a lot of pain! How does it work /difference between your signing and @schriftgestalt 's? I'll pull it in and update the binary. The repo is getting slightly bloated with binaries :-).

schriftgestalt commented 4 years ago

I codesign everthing manually beforehand, including the libs. The stuff in my repo should be signed correctly. But it is with my signature…

HinTak commented 4 years ago

The library is at bin/Darwin/libfreetype.6.dylib, which is baked into MacUI/FontValidator/FontValidator/FontValidator . It is still a two-step process, sign the lib, bake it into the binary, then sign the binary... I am just wondering if mac os wants the same signing person for the whole thing. Anyway, I'll bake it in to see if it works.

schriftgestalt commented 4 years ago

If the libraries are backed into the binary, then they don’t need to be code signed.

HinTak commented 4 years ago

It is a bit more complicated than that. The way the binary works, is that at the beginning of its running, it looks at itself and extract the baked-in library to a temporary directory, then, load it. It was made to work that way with mono 4.8, when apple started to stop binaries loading libraries from non-system places. anyway, if signing twice works (even if signing by two different people), then that's tedious but okay.

HinTak commented 4 years ago

@schriftgestalt @threeseat - okay, I updated the binary. Georg, if you are worried about giving/shipping libraries signed by somebody else - I have made some changes to my python script which examines (and modifies ) mac os x binaries to look at libfreetype before and after. It is slightly more complicated than the mono binary (which is single-arch, for 64-bit only), as i built the libfreetype bi-arch, as a fat binary. The signing process basically appends a signature block to the 32-bit section, and another signature block to the 64-bit section; The two sections are both padded to 4k's. So the difference before and after, are the two separate sections with or without the two new signature blocks, plus or minus padding, plus adjustments about locations in the three headers (the overall fat binary header, and the 32-bit/64-bit header). The padding itself is a bit complicated - sections in fat binaries are padded to 4k's, while signatures are appended after padded to 16-byte (or 8, not sure, I have only seen a few). i.e. before

Let's say fat needs to pad to 8, but signature appends after pad to 4:

before:
xxxxxxxx yyyyyyyy yyyyyyyy y******* zzzzzzzz zzzzzzzz zz******
after:
Xxxxxxxx Yyyyyyyy yyyyyyyy y***ssss ss****** Zzzzzzzz zzzzzzzz zz**SSSS SSS*****

Where x=fat header, y=32-bit section, z=64-bit section, *=padding, s and S the two signatures. X, Y, Z=modified.

Anyway, I am happy that the bulk of the code section (y and z) are the same, after signing. @schriftgestalt

schriftgestalt commented 4 years ago

For the UI app, it would be simpler to put the libraries into the bundle as load them as usual. That should be saver then the packing/unpacking.

Or build static libs and link them in directly (I do that in Glyphs so I know that it works).

HinTak commented 4 years ago

@schriftgestalt how are you with the new release? I have a few other things to do but those don't affect mac os x. So if the current binary (which has an embedded freetype signed by @threeseat ) can be signed by you, that's the next release.

schriftgestalt commented 4 years ago

I’m very busy right now. I’ll try to have a look tomorrow.

HinTak commented 4 years ago

@schriftgestalt that's okay - whenever you can. Let me know either way - it works or not.

schriftgestalt commented 4 years ago

It seems that the included libs need to be signed by me, too. I signed it, so now you need to repackaged the binary again.

But I would prefer that the dylibs would not be copied into the binary. Either link it statically, or just keep them next to the binary.

HinTak commented 4 years ago

Thanks a lot. I'll do it soon.

HinTak commented 4 years ago

@schriftgestalt Thanks for all the trouble. I have updated the binary now; if you could check that it works, sometimes soon, with a new GUI release, I'll put it up on the release page. We can then close this.

Let me try to explain the situation a bit about libfreetype. There are both technical and political reasons that the freetype customized code aren't going into freetype main any time soon; and in any case, freetype isn't a standard system component on Mac OS X anyway, so we'll always need our own copy. Many Mac users however, will have a copy of some version of libfreeype under /usr/local via homebrew, for example, for various reasons. As you might remember, the earlier releases uses DYLD_LIBRARY_PATH to specify where to find that our specific copy of freetype.

Several versions of Mac OS X ago, Apple announced that playing with DYLD_LIBRARY_PATH to re-direct library look-up are not recommended and should not be expected to work in coming releases; also it is neater to have just one visible piece, rather than 3+ loose pieces (a central binary, a bundled library, and a launch script which launches the binary with directions to find where the library is). So the mono people came up with the idea that the mono runtime would look for extra-stuff appended into the end of its own binary, unpacks them on launch, and open each of those pieces explicitly. This is quite neat, as everything is all in one binary.

With the requirement of code-signing, this idea is broken in two ways:

I have given issues surround this a lot of thoughts, and here are what I think we'd do in the long term:

schriftgestalt commented 4 years ago

This article describes how to specify the position of linked libraries: https://wincent.com/wiki/@executable_path,_@load_path_and_@rpath

This will not go away as it will break every single app. Can you point me to the deprecation warning you where speaking about?

And I still don’t understand why you don’t use static linking? That would properly include the library into the executable.

HinTak commented 4 years ago

This, for example: https://github.com/googlefonts/fontbakery/issues/2801

Static linking isn't quite applicable - remember much of FontVal is in C# and compiled to a form of byte code, then executed by a byte-code runtime. It is not obvious only because the runtime is built into the OS on windows, called dotnet. So some form of dynamic loading is always there, because part of it is not compiled into machine code. (or I might need to hack at mono itself some more...).

schriftgestalt commented 4 years ago

The example doesn't say anything about something being deprecated. I don’t fully understand what's going on but this is a misconfiguration rather than a technical limitation. Actually it might be a similar problem as we have with the fontVal binary.

But to launch, some part of the binary need to be Mach-O (that then loads the runtime). And that should be able to link to the lib.

All this copying of libraries into the temp folder looks like a massive security risk.

HinTak commented 4 years ago

There are many references to "System Integrity Protection breaks DYLD_LIBRARY_PATH" on a Internet search.

Yes, being able to load library from a non-system location is considered a security risk - Apple would like you to sign binaries and do notorization (upload your binary to be certified by them, as far as I understand it).

I am not saying it is not - but as this is Microsoft tech, you inherit some of Microsoft's lax thinking... And it being 20-year old tech too... So yeah, I like Fontval to have a more apple-centric design and a modern one too, but it is about 20-years too late :-(.

HinTak commented 4 years ago

To this day, you can still get a Microsoft app to preferably load a custom dll of the same name as a system one, just by copying your custom dll into the same folder. Obviously very different mind set...

HinTak commented 4 years ago

@schriftgestalt I have tagged 2.1.6 with a small update to get the FeatureParams things better as discussed on the opentype list. If you haven't made a new build yet, please use that sync to that.

schriftgestalt commented 4 years ago

It should work now. I uploaded https://github.com/schriftgestalt/Font-Validator/blob/master/MacUI/build/FontValidator0.1-8.zip

HinTak commented 4 years ago

@schriftgestalt since it had been out for a week or so and there are about 50 downloads and I have not heard otherwise, this can close... I'd like to split the mac guI part out as 'FontVal-MacGUI' or something similar - any suggestions? Basically I am planning to replace the directory with a submodule, and moving the GUI part to the bottom of its own directory and repo. Any suggestions for the name?

schriftgestalt commented 4 years ago

‘FontVal-MacGUI’ looks good.