blob: e3b33b86bb93f6ccc143c65c20907fc708952a33 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2024 The Cobalt Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tool for updating Cobalt SSL Certificates.
Usage:
update_certs.py -r <cobalt_repo> -b <openssl_bin> -p <pem_files>
Args:
cobalt_repo: The path to the Cobalt repository root.
openssl_bin: The path to the OpenSSL binary, defaults to "/usr/bin/openssl"
when not specified.
pem_files: The paths to the source pem files.
"""
import argparse
import os
import shutil
import subprocess
from typing import List
from typing import Set
_CERTS_PATH = 'cobalt/content/ssl/certs'
_BUILD_PATH = 'cobalt/network/certs.gni'
def SplitCerts(concatenated_cert_file: str) -> List[str]:
"""Split concatenated .pem file into a list of individual certs."""
begin_anchor = '-----BEGIN CERTIFICATE-----'
end_anchor = '-----END CERTIFICATE-----'
cert_list = []
current_cert = ''
in_cert = False
for line in concatenated_cert_file:
if not in_cert and line.find(begin_anchor) != -1:
in_cert = True
current_cert += line
elif in_cert and line.find(end_anchor) != -1:
in_cert = False
current_cert += line
cert_list.append(current_cert)
current_cert = ''
elif in_cert:
current_cert += line
return cert_list
def ComputeCertFilename(openssl_bin: str, cert: str) -> str:
"""Use x509 to get the hashed filename for the individual cert."""
command_args = [openssl_bin, 'x509', '-subject_hash', '-noout']
output = subprocess.check_output(command_args, input=cert.encode('utf-8'))
return output.decode().rstrip() + '.0'
def UpdateBuild(build_file: str, cert_filepaths: Set[str]) -> None:
cert_filepaths = list(cert_filepaths)
cert_filepaths.sort()
# Adds build file
cert_lines = ['network_certs = [\n']
cert_lines += [f' "//{path}",\n' for path in cert_filepaths]
cert_lines += [']\n']
with open(build_file, 'w', encoding='utf-8') as f:
f.writelines(cert_lines)
def UpdateCerts(certs_repo: str, openssl_bin: str, pem_files: str) -> None:
certs_path = os.path.join(certs_repo, _CERTS_PATH)
# Removes existing certs
if os.path.exists(certs_path):
shutil.rmtree(certs_path)
os.mkdir(certs_path)
# Adds certs
relative_filepaths = set()
for pem_file in pem_files:
cert_list = []
with open(pem_file, encoding='utf-8') as p:
cert_list = SplitCerts(p)
for cert in cert_list:
filename = ComputeCertFilename(openssl_bin, cert)
filepath = os.path.join(certs_path, filename)
relative_filepath = os.path.join(_CERTS_PATH, filename)
relative_filepaths.add(relative_filepath)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(cert)
# Updates certs.gni with updated certs
build_file = os.path.join(certs_repo, _BUILD_PATH)
UpdateBuild(build_file, relative_filepaths)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
'-r',
'--cobalt_repo',
type=str,
required=True,
help='The path to the Cobalt repository root.')
parser.add_argument(
'-b',
'--openssl_bin',
type=str,
required=False,
default='/usr/bin/openssl',
help='The path to the OpenSSL binary.')
parser.add_argument(
'-p',
'--pem_files',
type=str,
nargs='+',
required=True,
help='The paths to the source pem files.')
args = parser.parse_args()
UpdateCerts(args.cobalt_repo, args.openssl_bin, args.pem_files)
if __name__ == '__main__':
main()