• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2021 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
15load("@bazel_skylib//lib:paths.bzl", "paths")
16load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
17load("//build/bazel/product_config:product_variables_providing_rule.bzl", "ProductVariablesDepsInfo", "ProductVariablesInfo")
18
19AndroidAppCertificateInfo = provider(
20    "Info needed for Android app certificates",
21    fields = {
22        "pem": "Certificate .pem file",
23        "pk8": "Certificate .pk8 file",
24        "key_name": "Key name",
25    },
26)
27
28def _search_cert_files(cert_name, cert_files_to_search):
29    pk8 = None
30    pem = None
31    for file in cert_files_to_search:
32        if file.basename == cert_name + ".pk8":
33            pk8 = file
34        elif file.basename == cert_name + ".x509.pem":
35            pem = file
36    if not pk8 or not pem:
37        fail("Could not find .x509.pem and/or .pk8 file with name '%s' in the following files: %s" % (cert_name, cert_files_to_search))
38    return pk8, pem
39
40def _maybe_override(ctx, cert_name):
41    if not cert_name:
42        fail("cert_name cannot be None")
43
44    cert_overrides = ctx.attr._product_variables[ProductVariablesInfo].CertificateOverrides
45    if not cert_overrides:
46        return cert_name, False
47
48    apex_name = ctx.attr._apex_name[BuildSettingInfo].value
49    if not apex_name:
50        # Only override in the apex configuration, because the apex module name is used as the key for overriding
51        return cert_name, False
52
53    matches = [o for o in cert_overrides if o.split(":")[0] == apex_name]
54
55    if not matches:
56        # no matches, no override.
57        return cert_name, False
58
59    if len(matches) > 1:
60        fail("unexpected multiple certificate overrides for %s in: %s" % (apex_name, matches))
61
62    # e.g. test1_com.android.tzdata:com.google.android.tzdata5.certificate
63    new_cert_name = matches[0].split(":")[1]
64    return new_cert_name.removesuffix(".certificate"), True
65
66def _android_app_certificate_rule_impl(ctx):
67    cert_name = ctx.attr.certificate
68    pk8 = ctx.file.pk8
69    pem = ctx.file.pem
70
71    # Only override if the override mapping exists, otherwise we wouldn't be
72    # able to find the new certs.
73    overridden_cert_name, overridden = _maybe_override(ctx, cert_name)
74    if overridden:
75        cert_name = overridden_cert_name
76        cert_files_to_search = ctx.attr._product_variables[ProductVariablesDepsInfo].OverridingCertificateFiles
77        pk8, pem = _search_cert_files(cert_name, cert_files_to_search)
78
79    return [
80        AndroidAppCertificateInfo(pem = pem, pk8 = pk8, key_name = cert_name),
81    ]
82
83_android_app_certificate = rule(
84    implementation = _android_app_certificate_rule_impl,
85    attrs = {
86        "pem": attr.label(mandatory = True, allow_single_file = [".pem"]),
87        "pk8": attr.label(mandatory = True, allow_single_file = [".pk8"]),
88        "certificate": attr.string(mandatory = True),
89        "_apex_name": attr.label(default = "//build/bazel/rules/apex:apex_name"),
90        "_product_variables": attr.label(
91            default = "//build/bazel/product_config:product_vars",
92        ),
93        "_hardcoded_certs": attr.label(
94            default = "//build/make/target/product/security:android_certificate_directory",
95        ),
96    },
97)
98
99def android_app_certificate(
100        name,
101        certificate,
102        **kwargs):
103    "Bazel macro to correspond with the Android app certificate Soong module."
104
105    _android_app_certificate(
106        name = name,
107        pem = certificate + ".x509.pem",
108        pk8 = certificate + ".pk8",
109        certificate = certificate,
110        **kwargs
111    )
112
113default_cert_directory = "build/make/target/product/security"
114
115def _android_app_certificate_with_default_cert_impl(ctx):
116    product_var_cert = ctx.attr._product_variables[ProductVariablesInfo].DefaultAppCertificate
117
118    cert_name = ctx.attr.cert_name
119
120    if cert_name and product_var_cert:
121        cert_dir = paths.dirname(product_var_cert)
122    elif cert_name:
123        cert_dir = default_cert_directory
124    elif product_var_cert:
125        cert_name = paths.basename(product_var_cert)
126        cert_dir = paths.dirname(product_var_cert)
127    else:
128        cert_name = "testkey"
129        cert_dir = default_cert_directory
130
131    if cert_dir != default_cert_directory:
132        cert_files_to_search = ctx.attr._product_variables[ProductVariablesDepsInfo].DefaultAppCertificateFiles
133    else:
134        cert_files_to_search = ctx.files._hardcoded_certs
135
136    cert_name, overridden = _maybe_override(ctx, cert_name)
137    if overridden:
138        cert_files_to_search = ctx.attr._product_variables[ProductVariablesDepsInfo].OverridingCertificateFiles
139    pk8, pem = _search_cert_files(cert_name, cert_files_to_search)
140
141    return [
142        AndroidAppCertificateInfo(
143            pk8 = pk8,
144            pem = pem,
145            key_name = "//" + cert_dir + ":" + cert_name,
146        ),
147    ]
148
149android_app_certificate_with_default_cert = rule(
150    doc = """
151    This rule is the equivalent of an android_app_certificate, but uses the
152    certificate with the given name from a certain folder, or the default
153    certificate.
154
155    Modules can give a simple name of a certificate instead of a full label to
156    an android_app_certificate. This certificate will be looked for either in
157    the package determined by the DefaultAppCertificate product config variable,
158    or the hardcoded default directory. (build/make/target/product/security)
159
160    If a name is not given, it will fall back to using the certificate termined
161    by DefaultAppCertificate. (DefaultAppCertificate can function as both the
162    default certificate to use if none is specified, and the folder to look for
163    certificates in)
164
165    If neither the name nor DefaultAppCertificate is given,
166    build/make/target/product/security/testkey.{pem,pk8} will be used.
167
168    Since this rule is intended to be used from other macros, it's common to have
169    multiple android_app_certificate targets pointing to the same pem/pk8 files.
170    """,
171    implementation = _android_app_certificate_with_default_cert_impl,
172    attrs = {
173        "cert_name": attr.string(),
174        "_product_variables": attr.label(
175            default = "//build/bazel/product_config:product_vars",
176        ),
177        "_hardcoded_certs": attr.label(
178            default = "//build/make/target/product/security:android_certificate_directory",
179        ),
180    },
181)
182