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	"sort"
20	"strings"
21
22	"android/soong/android"
23
24	"github.com/google/blueprint/proptools"
25)
26
27var String = proptools.String
28
29func init() {
30	registerApexKeyBuildComponents(android.InitRegistrationContext)
31}
32
33func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
34	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
35	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
36}
37
38type apexKey struct {
39	android.ModuleBase
40
41	properties apexKeyProperties
42
43	publicKeyFile  android.Path
44	privateKeyFile android.Path
45
46	keyName string
47}
48
49type apexKeyProperties struct {
50	// Path or module to the public key file in avbpubkey format. Installed to the device.
51	// Base name of the file is used as the ID for the key.
52	Public_key *string `android:"path"`
53	// Path or module to the private key file in pem format. Used to sign APEXs.
54	Private_key *string `android:"path"`
55
56	// Whether this key is installable to one of the partitions. Defualt: true.
57	Installable *bool
58}
59
60func ApexKeyFactory() android.Module {
61	module := &apexKey{}
62	module.AddProperties(&module.properties)
63	android.InitAndroidArchModule(module, android.HostAndDeviceDefault, android.MultilibCommon)
64	return module
65}
66
67func (m *apexKey) installable() bool {
68	return false
69}
70
71func (m *apexKey) GenerateAndroidBuildActions(ctx android.ModuleContext) {
72	// If the keys are from other modules (i.e. :module syntax) respect it.
73	// Otherwise, try to locate the key files in the default cert dir or
74	// in the local module dir
75	if android.SrcIsModule(String(m.properties.Public_key)) != "" {
76		m.publicKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
77	} else {
78		m.publicKeyFile = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Public_key))
79		// If not found, fall back to the local key pairs
80		if !android.ExistentPathForSource(ctx, m.publicKeyFile.String()).Valid() {
81			m.publicKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
82		}
83	}
84
85	if android.SrcIsModule(String(m.properties.Private_key)) != "" {
86		m.privateKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
87	} else {
88		m.privateKeyFile = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Private_key))
89		if !android.ExistentPathForSource(ctx, m.privateKeyFile.String()).Valid() {
90			m.privateKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
91		}
92	}
93
94	pubKeyName := m.publicKeyFile.Base()[0 : len(m.publicKeyFile.Base())-len(m.publicKeyFile.Ext())]
95	privKeyName := m.privateKeyFile.Base()[0 : len(m.privateKeyFile.Base())-len(m.privateKeyFile.Ext())]
96
97	if m.properties.Public_key != nil && m.properties.Private_key != nil && pubKeyName != privKeyName {
98		ctx.ModuleErrorf("public_key %q (keyname:%q) and private_key %q (keyname:%q) do not have same keyname",
99			m.publicKeyFile.String(), pubKeyName, m.privateKeyFile, privKeyName)
100		return
101	}
102	m.keyName = pubKeyName
103}
104
105////////////////////////////////////////////////////////////////////////
106// apex_keys_text
107type apexKeysText struct {
108	output android.OutputPath
109}
110
111func (s *apexKeysText) GenerateBuildActions(ctx android.SingletonContext) {
112	s.output = android.PathForOutput(ctx, "apexkeys.txt")
113	type apexKeyEntry struct {
114		name                 string
115		presigned            bool
116		publicKey            string
117		privateKey           string
118		containerCertificate string
119		containerPrivateKey  string
120		partition            string
121	}
122	toString := func(e apexKeyEntry) string {
123		format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q\n"
124		if e.presigned {
125			return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition)
126		} else {
127			return fmt.Sprintf(format, e.name, e.publicKey, e.privateKey, e.containerCertificate, e.containerPrivateKey, e.partition)
128		}
129	}
130
131	apexKeyMap := make(map[string]apexKeyEntry)
132	ctx.VisitAllModules(func(module android.Module) {
133		if m, ok := module.(*apexBundle); ok && m.Enabled() && m.installable() {
134			pem, key := m.getCertificateAndPrivateKey(ctx)
135			apexKeyMap[m.Name()] = apexKeyEntry{
136				name:                 m.Name() + ".apex",
137				presigned:            false,
138				publicKey:            m.publicKeyFile.String(),
139				privateKey:           m.privateKeyFile.String(),
140				containerCertificate: pem.String(),
141				containerPrivateKey:  key.String(),
142				partition:            m.PartitionTag(ctx.DeviceConfig()),
143			}
144		}
145	})
146
147	// Find prebuilts and let them override apexBundle if they are preferred
148	ctx.VisitAllModules(func(module android.Module) {
149		if m, ok := module.(*Prebuilt); ok && m.Enabled() && m.installable() &&
150			m.Prebuilt().UsePrebuilt() {
151			apexKeyMap[m.BaseModuleName()] = apexKeyEntry{
152				name:      m.InstallFilename(),
153				presigned: true,
154				partition: m.PartitionTag(ctx.DeviceConfig()),
155			}
156		}
157	})
158
159	// Find apex_set and let them override apexBundle or prebuilts. This is done in a separate pass
160	// so that apex_set are not overridden by prebuilts.
161	ctx.VisitAllModules(func(module android.Module) {
162		if m, ok := module.(*ApexSet); ok && m.Enabled() {
163			entry := apexKeyEntry{
164				name:      m.InstallFilename(),
165				presigned: true,
166				partition: m.PartitionTag(ctx.DeviceConfig()),
167			}
168			apexKeyMap[m.BaseModuleName()] = entry
169		}
170	})
171
172	// iterating over map does not give consistent ordering in golang
173	var moduleNames []string
174	for key, _ := range apexKeyMap {
175		moduleNames = append(moduleNames, key)
176	}
177	sort.Strings(moduleNames)
178
179	var filecontent strings.Builder
180	for _, name := range moduleNames {
181		filecontent.WriteString(toString(apexKeyMap[name]))
182	}
183	android.WriteFileRule(ctx, s.output, filecontent.String())
184}
185
186func apexKeysTextFactory() android.Singleton {
187	return &apexKeysText{}
188}
189
190func (s *apexKeysText) MakeVars(ctx android.MakeVarsContext) {
191	ctx.Strict("SOONG_APEX_KEYS_FILE", s.output.String())
192}
193