| # Code signing | |
| This covers the process of how to add / update code signing metadata of flutter | |
| engine binaries. | |
| ## Overview | |
| Flutter engine binaries are built with GN and ninja, referencing pre-defined | |
| configurations such as ci/builders | |
| [JSON files](https://github.com/flutter/flutter/blob/main/engine/src/flutter/ci/builders/mac_host_engine.json). | |
| During flutter releases, engineers need to code sign mac engine binaries to | |
| assure users that they come from a known source, have not been tampered with, | |
| and should not be quarantined by Gatekeepers. | |
| Each of the Flutter engine binaries are either code signed with entitlements, or | |
| code signed without entitlements. (An entitlement, along with information from | |
| the developer account, grant particular permissions to binaries, such as | |
| capability to access the user's home automation network.) For example, impellerc | |
| is code signed with flutter entitlements, whereas .dylib files are usually code | |
| signed without entitlements. | |
| ## Add / Update code signing metadata | |
| ### Glossary | |
| 1. BUILD.gn files: files that include build rules of GN targets. An example is | |
| the | |
| [BUILD.gn file of flutter engine](https://github.com/flutter/flutter/blob/main/engine/src/flutter/BUILD.gn). | |
| 2. leaf node of an engine binary: the minimal gn target that could produce such | |
| an engine binary. That is, this target does not have any dependencies on | |
| other gn targets that could build this engine binary. | |
| 3. dependencies: Every gn target could have dependencies on other gn targets. | |
| The dependency of a gn target is defined in the `deps` field of the target's | |
| build rule. | |
| ### ways to generate engine binary | |
| Generally, there are two ways to generate an engine binary: | |
| 1. Through build rules defined in BUILD.gn files. | |
| 2. Through global generator scripts. (these scripts are normally .py files) | |
| To distinguish between the two, an engine binary is built through global | |
| generator if it is listed in the `archives` -> `destination` field of the | |
| builder JSON | |
| ([mac_ios_engine.json](https://github.com/flutter/flutter/blob/main/engine/src/flutter/ci/builders/mac_ios_engine.json) | |
| or | |
| [mac_host_engine.json](https://github.com/flutter/flutter/blob/main/engine/src/flutter/ci/builders/mac_host_engine.json)). | |
| For example, `darwin-x64/FlutterEmbedder.framework.zip`. Whereas binaries built | |
| with BUILD.gn files are listed among the `builds` field of the JSON file. For | |
| example, `darwin-x64/artifacts.zip`. We will provide examples for both | |
| scenarios. | |
| ### To add / update code signing metadata in BUILD.gn files: | |
| 1. Find the leaf node where the target engine binary is built. To do so, | |
| Recursively trace the `deps` field of the engine artifact. The paths in | |
| `deps` field of the GN target correspond to the paths of other GN targets | |
| that are dependencies of the current GN target. | |
| 2. Add / Update the `metadata` field of the leaf node. For a new engine binary: | |
| 2.1 if it should be code signed with entitlements, add [the name of the | |
| engine binary] to the `entitlement_file_path` field in `metadata` . | |
| 2.2 if the binary shouldn't be code signed with entitlements, add [the name | |
| of the engine binary] to the `without_entitlement_file_path` field in | |
| `metadata` . | |
| 3. If a `entitlement_file_path` or a `without_entitlement_file_path` field does | |
| not exist: | |
| **note**: this step is only needed if the target includes solely binaries | |
| that have never been code signed before. This step also requires some | |
| background on flutter engine and gn build rules. | |
| Add a `metadata` field in the gn target of the leaf node, and put the name | |
| of the binary in this field. e.g. | |
| ``` | |
| metadata = { | |
| entitlement_file_path = [ "libtessellator.dylib" ] | |
| } | |
| ``` | |
| In the same file that produces the engine artifact(zip file), add a build | |
| rule to collect the data keys. e.g. | |
| ``` | |
| generated_file("artifacts_entitlement_config") { | |
| outputs = [ "$target_gen_dir/entitlements.txt" ] | |
| data_keys = [ "entitlement_file_path" ] | |
| deps = [ "//flutter/lib/snapshot:generate_snapshot_bin" ] | |
| if (flutter_runtime_mode == "debug") { | |
| deps += [ | |
| "//flutter/impeller/compiler:impellerc", | |
| "//flutter/impeller/tessellator:tessellator_shared", | |
| "//flutter/shell/testing:testing", | |
| "//flutter/tools/path_ops:path_ops", | |
| ] | |
| } | |
| } | |
| ``` | |
| Finally, embed the file with collected data keys in the zip artifact. e.g. | |
| ``` | |
| if (host_os == "mac") { | |
| deps += [ ":artifacts_entitlement_config" ] | |
| files += [ | |
| { | |
| source = "$target_gen_dir/entitlements.txt" | |
| destination = "entitlements.txt" | |
| }, | |
| ] | |
| } | |
| ``` | |
| #### Example | |
| Suppose impellerc is a binary that exist in a zip bundle called artifacts.zip. | |
| Then impellerc is the name of the binary, and artifacts.zip is the flutter | |
| engine artifact. | |
| 1. Following step 1, the `deps` field of the GN target of artifacts.zip | |
| includes the path of impeller dependency: | |
| `//flutter/impeller/compiler:impellerc`. Following this path, we locate the | |
| GN file at `flutter/impeller/compiler/BUILD.gn`, and find the leaf node that | |
| builds impellerc: `impeller_component("impellerc")`. | |
| 2. Following step 2, since `impellerc` should be code signed with entitlements, | |
| we go to the `metadata` field of the impellerc target, and add the name | |
| `impellerc` to the `entitlement_file_path` array inside the `metadata` | |
| field. | |
| You can reference the | |
| [BUILD.gn file of impellerc](https://github.com/flutter/flutter/blob/main/engine/src/flutter/impeller/compiler/BUILD.gn). | |
| ### To add / update code signing metadata in global generator files: | |
| 1. Find the generator script path listed under `generators` -> `tasks` -> | |
| `script` of the ci/builder JSON files | |
| ([mac_ios_engine.json](https://github.com/flutter/flutter/blob/main/engine/src/flutter/ci/builders/mac_ios_engine.json) | |
| or | |
| [mac_host_engine.json](https://github.com/flutter/flutter/blob/main/engine/src/flutter/ci/builders/mac_host_engine.json)). | |
| The generator script related to iOS is located at | |
| `sky/tools/create_ios_framework.py`, and generator script related to | |
| macOS is located at `sky/tools/create_macos_framework.py`. | |
| 2. Add / Update the variables ending with `with_entitlements` / | |
| `without_entitlements` suffix from the generator script you found in step | |
| one. | |
| As an example, you can find variables `ios_file_without_entitlements` and | |
| `ios_file_with_entitlements` in sky/tools/create_ios_framework.py; and | |
| find variables `filepath_without_entitlements` and | |
| `filepath_with_entitlements` in sky/tools/create_macos_framework.py | |
| 2.1 if the binary should be code signed with entitlements, add [the name of | |
| the binary] to the variable name with the `with_entitlements` suffix. | |
| (`ios_file_with_entitlements` or `filepath_with_entitlements` depending on | |
| which script) | |
| 2.2 if the binary shouldn't be code signed with entitlements, add [the name | |
| of the binary] to the variable name with the `without_entitlements` suffix. | |
| #### Example | |
| Suppose `Flutter.xcframework/ios-arm64/Flutter.framework/Flutter` is a binary | |
| that exist in a zip bundle called `ios/artifacts.zip`. | |
| 1. Following step 1, in | |
| [mac_ios_engine.json](https://github.com/flutter/flutter/blob/main/engine/src/flutter/ci/builders/mac_ios_engine.json), | |
| it builds the artifact with the | |
| `flutter/sky/tools/create_ios_framework.py` script. | |
| 2. Following step 2, since | |
| `Flutter.xcframework/ios-arm64/Flutter.framework/Flutter` shouldn't be code | |
| signed with entitlements, we add the binary name | |
| `Flutter.xcframework/ios-arm64/Flutter.framework/Flutter` to the | |
| `ios_file_without_entitlements` variable. | |
| You can reference the generator script | |
| [create_ios_framework.py](https://github.com/flutter/flutter/blob/main/engine/src/flutter/sky/tools/create_ios_framework.py). | |
| ## Code signing artifacts other than flutter engine binaries | |
| The code signing functionality is implemented as [a recipe module in flutter recipes](https://cs.opensource.google/flutter/recipes/+/master:recipe_modules/signing/api.py). Therefore it can also be used to | |
| code sign arbitrary flutter artifacts built through recipe, for example, flutter iOS usb dependencies. | |
| To code sign, after the artifacts are built, pass the file paths into | |
| the code signing recipe module and invoke the function. An example is [how engine V2 invokes the code signing recipe module](https://cs.opensource.google/flutter/recipes/+/master:recipes/engine_v2/engine_v2.py;l=197-212). |