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