1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package filesystem 16 17import ( 18 "fmt" 19 "path/filepath" 20 "strings" 21 22 "android/soong/android" 23) 24 25type fsverityProperties struct { 26 // Patterns of files for fsverity metadata generation. For each matched file, a .fsv_meta file 27 // will be generated and included to the filesystem image. 28 // etc/security/fsverity/BuildManifest.apk will also be generated which contains information 29 // about generated .fsv_meta files. 30 Inputs []string 31 32 // APK libraries to link against, for etc/security/fsverity/BuildManifest.apk 33 Libs []string `android:"path"` 34} 35 36func (f *filesystem) writeManifestGeneratorListFile(ctx android.ModuleContext, outputPath android.WritablePath, matchedSpecs []android.PackagingSpec, rebasedDir android.OutputPath) { 37 var buf strings.Builder 38 for _, spec := range matchedSpecs { 39 buf.WriteString(rebasedDir.Join(ctx, spec.RelPathInPackage()).String()) 40 buf.WriteRune('\n') 41 } 42 android.WriteFileRuleVerbatim(ctx, outputPath, buf.String()) 43} 44 45func (f *filesystem) buildFsverityMetadataFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir android.OutputPath, rebasedDir android.OutputPath) { 46 match := func(path string) bool { 47 for _, pattern := range f.properties.Fsverity.Inputs { 48 if matched, err := filepath.Match(pattern, path); matched { 49 return true 50 } else if err != nil { 51 ctx.PropertyErrorf("fsverity.inputs", "bad pattern %q", pattern) 52 return false 53 } 54 } 55 return false 56 } 57 58 var matchedSpecs []android.PackagingSpec 59 for _, relPath := range android.SortedKeys(specs) { 60 if match(relPath) { 61 matchedSpecs = append(matchedSpecs, specs[relPath]) 62 } 63 } 64 65 if len(matchedSpecs) == 0 { 66 return 67 } 68 69 fsverityPath := ctx.Config().HostToolPath(ctx, "fsverity") 70 71 // STEP 1: generate .fsv_meta 72 var sb strings.Builder 73 sb.WriteString("set -e\n") 74 for _, spec := range matchedSpecs { 75 // srcPath is copied by CopySpecsToDir() 76 srcPath := rebasedDir.Join(ctx, spec.RelPathInPackage()) 77 destPath := rebasedDir.Join(ctx, spec.RelPathInPackage()+".fsv_meta") 78 builder.Command(). 79 BuiltTool("fsverity_metadata_generator"). 80 FlagWithInput("--fsverity-path ", fsverityPath). 81 FlagWithArg("--signature ", "none"). 82 FlagWithArg("--hash-alg ", "sha256"). 83 FlagWithArg("--output ", destPath.String()). 84 Text(srcPath.String()) 85 f.appendToEntry(ctx, destPath) 86 } 87 88 // STEP 2: generate signed BuildManifest.apk 89 // STEP 2-1: generate build_manifest.pb 90 manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity_manifest.list") 91 f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath, matchedSpecs, rebasedDir) 92 assetsPath := android.PathForModuleOut(ctx, "fsverity_manifest/assets") 93 manifestPbPath := assetsPath.Join(ctx, "build_manifest.pb") 94 builder.Command().Text("rm -rf " + assetsPath.String()) 95 builder.Command().Text("mkdir -p " + assetsPath.String()) 96 builder.Command(). 97 BuiltTool("fsverity_manifest_generator"). 98 FlagWithInput("--fsverity-path ", fsverityPath). 99 FlagWithArg("--base-dir ", rootDir.String()). 100 FlagWithArg("--output ", manifestPbPath.String()). 101 FlagWithInput("@", manifestGeneratorListPath) 102 103 f.appendToEntry(ctx, manifestPbPath) 104 f.appendToEntry(ctx, manifestGeneratorListPath) 105 106 // STEP 2-2: generate BuildManifest.apk (unsigned) 107 apkNameSuffix := "" 108 if f.PartitionType() == "system_ext" { 109 //https://source.corp.google.com/h/googleplex-android/platform/build/+/e392d2b486c2d4187b20a72b1c67cc737ecbcca5:core/Makefile;l=3410;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0 110 apkNameSuffix = "SystemExt" 111 } 112 apkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk", apkNameSuffix)) 113 idsigPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk.idsig", apkNameSuffix)) 114 manifestTemplatePath := android.PathForSource(ctx, "system/security/fsverity/AndroidManifest.xml") 115 libs := android.PathsForModuleSrc(ctx, f.properties.Fsverity.Libs) 116 117 minSdkVersion := ctx.Config().PlatformSdkCodename() 118 if minSdkVersion == "REL" { 119 minSdkVersion = ctx.Config().PlatformSdkVersion().String() 120 } 121 122 unsignedApkCommand := builder.Command(). 123 BuiltTool("aapt2"). 124 Text("link"). 125 FlagWithOutput("-o ", apkPath). 126 FlagWithArg("-A ", assetsPath.String()) 127 for _, lib := range libs { 128 unsignedApkCommand.FlagWithInput("-I ", lib) 129 } 130 unsignedApkCommand. 131 FlagWithArg("--min-sdk-version ", minSdkVersion). 132 FlagWithArg("--version-code ", ctx.Config().PlatformSdkVersion().String()). 133 FlagWithArg("--version-name ", ctx.Config().AppsDefaultVersionName()). 134 FlagWithInput("--manifest ", manifestTemplatePath). 135 Text(" --rename-manifest-package com.android.security.fsverity_metadata." + f.partitionName()) 136 137 f.appendToEntry(ctx, apkPath) 138 139 // STEP 2-3: sign BuildManifest.apk 140 pemPath, keyPath := ctx.Config().DefaultAppCertificate(ctx) 141 builder.Command(). 142 BuiltTool("apksigner"). 143 Text("sign"). 144 FlagWithArg("--in ", apkPath.String()). 145 FlagWithInput("--cert ", pemPath). 146 FlagWithInput("--key ", keyPath). 147 ImplicitOutput(idsigPath) 148 149 f.appendToEntry(ctx, idsigPath) 150} 151