• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2018 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 apex
16
17import (
18	"fmt"
19
20	"android/soong/android"
21	"github.com/google/blueprint"
22	"github.com/google/blueprint/proptools"
23)
24
25var String = proptools.String
26
27func init() {
28	registerApexKeyBuildComponents(android.InitRegistrationContext)
29}
30
31func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
32	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
33	ctx.RegisterParallelSingletonModuleType("all_apex_certs", allApexCertsFactory)
34}
35
36type ApexKeyInfo struct {
37	PublicKeyFile  android.Path
38	PrivateKeyFile android.Path
39}
40
41var ApexKeyInfoProvider = blueprint.NewProvider[ApexKeyInfo]()
42
43type apexKey struct {
44	android.ModuleBase
45
46	properties apexKeyProperties
47
48	publicKeyFile  android.Path
49	privateKeyFile android.Path
50}
51
52type apexKeyProperties struct {
53	// Path or module to the public key file in avbpubkey format. Installed to the device.
54	// Base name of the file is used as the ID for the key.
55	Public_key *string `android:"path"`
56	// Path or module to the private key file in pem format. Used to sign APEXs.
57	Private_key *string `android:"path"`
58
59	// Whether this key is installable to one of the partitions. Defualt: true.
60	Installable *bool
61}
62
63func ApexKeyFactory() android.Module {
64	module := &apexKey{}
65	module.AddProperties(&module.properties)
66	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
67	return module
68}
69
70func (m *apexKey) installable() bool {
71	return false
72}
73
74func (m *apexKey) GenerateAndroidBuildActions(ctx android.ModuleContext) {
75	// If the keys are from other modules (i.e. :module syntax) respect it.
76	// Otherwise, try to locate the key files in the default cert dir or
77	// in the local module dir
78	if android.SrcIsModule(String(m.properties.Public_key)) != "" {
79		m.publicKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
80	} else {
81		m.publicKeyFile = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Public_key))
82		// If not found, fall back to the local key pairs
83		if !android.ExistentPathForSource(ctx, m.publicKeyFile.String()).Valid() {
84			m.publicKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
85		}
86	}
87
88	if android.SrcIsModule(String(m.properties.Private_key)) != "" {
89		m.privateKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
90	} else {
91		m.privateKeyFile = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Private_key))
92		if !android.ExistentPathForSource(ctx, m.privateKeyFile.String()).Valid() {
93			m.privateKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
94		}
95	}
96
97	pubKeyName := m.publicKeyFile.Base()[0 : len(m.publicKeyFile.Base())-len(m.publicKeyFile.Ext())]
98	privKeyName := m.privateKeyFile.Base()[0 : len(m.privateKeyFile.Base())-len(m.privateKeyFile.Ext())]
99
100	if m.properties.Public_key != nil && m.properties.Private_key != nil && pubKeyName != privKeyName {
101		ctx.ModuleErrorf("public_key %q (keyname:%q) and private_key %q (keyname:%q) do not have same keyname",
102			m.publicKeyFile.String(), pubKeyName, m.privateKeyFile, privKeyName)
103		return
104	}
105
106	android.SetProvider(ctx, ApexKeyInfoProvider, ApexKeyInfo{
107		PublicKeyFile:  m.publicKeyFile,
108		PrivateKeyFile: m.privateKeyFile,
109	})
110}
111
112type apexKeyEntry struct {
113	name                 string
114	presigned            bool
115	publicKey            string
116	privateKey           string
117	containerCertificate string
118	containerPrivateKey  string
119	partition            string
120	signTool             string
121}
122
123func (e apexKeyEntry) String() string {
124	signTool := ""
125	if e.signTool != "" {
126		signTool = fmt.Sprintf(" sign_tool=%q", e.signTool)
127	}
128	format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q%s\n"
129	if e.presigned {
130		return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition, signTool)
131	} else {
132		return fmt.Sprintf(format, e.name, e.publicKey, e.privateKey, e.containerCertificate, e.containerPrivateKey, e.partition, signTool)
133	}
134}
135
136func apexKeyEntryFor(ctx android.ModuleContext, module android.Module) apexKeyEntry {
137	switch m := module.(type) {
138	case *apexBundle:
139		pem, key := m.getCertificateAndPrivateKey(ctx)
140		return apexKeyEntry{
141			name:                 m.Name() + ".apex",
142			presigned:            false,
143			publicKey:            m.publicKeyFile.String(),
144			privateKey:           m.privateKeyFile.String(),
145			containerCertificate: pem.String(),
146			containerPrivateKey:  key.String(),
147			partition:            m.PartitionTag(ctx.DeviceConfig()),
148			signTool:             proptools.String(m.properties.Custom_sign_tool),
149		}
150	case *Prebuilt:
151		return apexKeyEntry{
152			name:      m.InstallFilename(),
153			presigned: true,
154			partition: m.PartitionTag(ctx.DeviceConfig()),
155		}
156	case *ApexSet:
157		return apexKeyEntry{
158			name:      m.InstallFilename(),
159			presigned: true,
160			partition: m.PartitionTag(ctx.DeviceConfig()),
161		}
162	}
163	panic(fmt.Errorf("unknown type(%t) for apexKeyEntry", module))
164}
165
166func writeApexKeys(ctx android.ModuleContext, module android.Module) android.WritablePath {
167	path := android.PathForModuleOut(ctx, "apexkeys.txt")
168	entry := apexKeyEntryFor(ctx, module)
169	android.WriteFileRuleVerbatim(ctx, path, entry.String())
170	return path
171}
172
173var (
174	pemToDer = pctx.AndroidStaticRule("pem_to_der",
175		blueprint.RuleParams{
176			Command:     `openssl x509 -inform PEM -outform DER -in $in -out $out`,
177			Description: "Convert certificate from PEM to DER format",
178		},
179	)
180)
181
182// all_apex_certs is a singleton module that collects the certs of all apexes in the tree.
183// It provides two types of output files
184// 1. .pem: This is usually the checked-in x509 certificate in PEM format
185// 2. .der: This is DER format of the certificate, and is generated from the PEM certificate using `openssl x509`
186func allApexCertsFactory() android.SingletonModule {
187	m := &allApexCerts{}
188	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
189	return m
190}
191
192type allApexCerts struct {
193	android.SingletonModuleBase
194}
195
196func (_ *allApexCerts) GenerateAndroidBuildActions(ctx android.ModuleContext) {
197	var avbpubkeys android.Paths
198	var certificatesPem android.Paths
199	ctx.VisitDirectDeps(func(m android.Module) {
200		if apex, ok := m.(*apexBundle); ok {
201			pem, _ := apex.getCertificateAndPrivateKey(ctx)
202			if !android.ExistentPathForSource(ctx, pem.String()).Valid() {
203				if ctx.Config().AllowMissingDependencies() {
204					return
205				} else {
206					ctx.ModuleErrorf("Path %s is not valid\n", pem.String())
207				}
208			}
209			certificatesPem = append(certificatesPem, pem)
210			// avbpubkey for signing the apex payload
211			avbpubkeys = append(avbpubkeys, apex.publicKeyFile)
212		}
213	})
214	certificatesPem = android.SortedUniquePaths(certificatesPem) // For hermiticity
215	avbpubkeys = android.SortedUniquePaths(avbpubkeys)           // For hermiticity
216	var certificatesDer android.Paths
217	for index, certificatePem := range certificatesPem {
218		certificateDer := android.PathForModuleOut(ctx, fmt.Sprintf("x509.%v.der", index))
219		ctx.Build(pctx, android.BuildParams{
220			Rule:   pemToDer,
221			Input:  certificatePem,
222			Output: certificateDer,
223		})
224		certificatesDer = append(certificatesDer, certificateDer)
225	}
226	ctx.SetOutputFiles(certificatesPem, ".pem")
227	ctx.SetOutputFiles(certificatesDer, ".der")
228	ctx.SetOutputFiles(avbpubkeys, ".avbpubkey")
229}
230
231func (_ *allApexCerts) GenerateSingletonBuildActions(ctx android.SingletonContext) {
232}
233