Introduce a singleton module to collect apex certs
`all_apex_certs` will provide two kinds of output files:
1. x509 certificate in pem format
2. x509 certificate in der format
filenames of the certs are not part of `all_apex_certs` api. In fact,
the der certs will be named with int indexes.
This singleton module will be used by `CtsSecurityTestCases`
(specifically `PackageSignatureTest`) to enforce that the apexes are
signed with release keys.
To implement this, `ctx.ReverseDepenendcy` will be used in deps mutator
of apex. An alternative would have been to use `ctx.VisitAllDeps` in
`GenerateAndroidBuildActions` of the singleton, but this would make it
unusable in the cts test (circular dep).
Make has a similar implementation that collects the certs of apexes and
dists that file. This has been kept separate for now to prevent b/304914238
Bug: 329299639
Test: m nothing --no-skip-soong-tests
Change-Id: I742d8ae1ccc344a78ae04263382750508b2aedec
diff --git a/apex/apex.go b/apex/apex.go
index b0d2b54..aeac0b7 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -888,8 +888,20 @@
ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
+
+ // Add a reverse dependency to all_apex_certs singleton module.
+ // all_apex_certs will use this dependency to collect the certificate of this apex.
+ ctx.AddReverseDependency(ctx.Module(), allApexCertsDepTag, "all_apex_certs")
}
+type allApexCertsDependencyTag struct {
+ blueprint.DependencyTag
+}
+
+func (_ allApexCertsDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
+var allApexCertsDepTag = allApexCertsDependencyTag{}
+
// DepsMutator for the overridden properties.
func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
if a.overridableProperties.Allowed_files != nil {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 81a62d9..f612f7e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3585,7 +3585,11 @@
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
- }`)
+ }`,
+ android.MockFS{
+ "vendor/foo/devkeys/testkey.x509.pem": nil,
+ }.AddToFixture(),
+ )
rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk")
expected := "vendor/foo/devkeys/testkey.x509.pem vendor/foo/devkeys/testkey.pk8"
if actual := rule.Args["certificates"]; actual != expected {
diff --git a/apex/key.go b/apex/key.go
index e4214f0..9fa9d1e 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -18,6 +18,7 @@
"fmt"
"android/soong/android"
+ "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -29,6 +30,7 @@
func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
+ ctx.RegisterParallelSingletonModuleType("all_apex_certs", allApexCertsFactory)
}
type apexKey struct {
@@ -155,3 +157,59 @@
android.WriteFileRuleVerbatim(ctx, path, entry.String())
return path
}
+
+var (
+ pemToDer = pctx.AndroidStaticRule("pem_to_der",
+ blueprint.RuleParams{
+ Command: `openssl x509 -inform PEM -outform DER -in $in -out $out`,
+ Description: "Convert certificate from PEM to DER format",
+ },
+ )
+)
+
+// all_apex_certs is a singleton module that collects the certs of all apexes in the tree.
+// It provides two types of output files
+// 1. .pem: This is usually the checked-in x509 certificate in PEM format
+// 2. .der: This is DER format of the certificate, and is generated from the PEM certificate using `openssl x509`
+func allApexCertsFactory() android.SingletonModule {
+ m := &allApexCerts{}
+ android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ return m
+}
+
+type allApexCerts struct {
+ android.SingletonModuleBase
+}
+
+func (_ *allApexCerts) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ var certificatesPem android.Paths
+ ctx.VisitDirectDeps(func(m android.Module) {
+ if apex, ok := m.(*apexBundle); ok {
+ pem, _ := apex.getCertificateAndPrivateKey(ctx)
+ if !android.ExistentPathForSource(ctx, pem.String()).Valid() {
+ if ctx.Config().AllowMissingDependencies() {
+ return
+ } else {
+ ctx.ModuleErrorf("Path %s is not valid\n", pem.String())
+ }
+ }
+ certificatesPem = append(certificatesPem, pem)
+ }
+ })
+ certificatesPem = android.SortedUniquePaths(certificatesPem) // For hermiticity
+ var certificatesDer android.Paths
+ for index, certificatePem := range certificatesPem {
+ certificateDer := android.PathForModuleOut(ctx, fmt.Sprintf("x509.%v.der", index))
+ ctx.Build(pctx, android.BuildParams{
+ Rule: pemToDer,
+ Input: certificatePem,
+ Output: certificateDer,
+ })
+ certificatesDer = append(certificatesDer, certificateDer)
+ }
+ ctx.SetOutputFiles(certificatesPem, ".pem")
+ ctx.SetOutputFiles(certificatesDer, ".der")
+}
+
+func (_ *allApexCerts) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+}
diff --git a/apex/testing.go b/apex/testing.go
index 63c5b69..a22f640 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -22,6 +22,9 @@
android.FixtureRegisterWithContext(registerApexBuildComponents),
android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
android.FixtureRegisterWithContext(registerApexDepsInfoComponents),
+ android.FixtureAddTextFile("all_apex_certs/Android.bp", `
+ all_apex_certs { name: "all_apex_certs" }
+ `),
// Additional files needed in tests that disallow non-existent source files.
// This includes files that are needed by all, or at least most, instances of an apex module type.
android.MockFS{
@@ -30,6 +33,8 @@
"build/soong/scripts/gen_ndk_backedby_apex.sh": nil,
// Needed by prebuilt_apex.
"build/soong/scripts/unpack-prebuilt-apex.sh": nil,
+ // Needed by all_apex_certs
+ "build/make/target/product/security/testkey.x509.pem": nil,
}.AddToFixture(),
android.PrepareForTestWithBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION", testDefaultUpdatableModuleVersion),
)