154aa6d63Sopenharmony_ci#!/usr/bin/env python
254aa6d63Sopenharmony_ci# coding=utf-8
354aa6d63Sopenharmony_ci##############################################
454aa6d63Sopenharmony_ci# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
554aa6d63Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
654aa6d63Sopenharmony_ci# you may not use this file except in compliance with the License.
754aa6d63Sopenharmony_ci# You may obtain a copy of the License at
854aa6d63Sopenharmony_ci#
954aa6d63Sopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
1054aa6d63Sopenharmony_ci#
1154aa6d63Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
1254aa6d63Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
1354aa6d63Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1454aa6d63Sopenharmony_ci# See the License for the specific language governing permissions and
1554aa6d63Sopenharmony_ci# limitations under the License.
1654aa6d63Sopenharmony_ci##############################################
1754aa6d63Sopenharmony_ciimport json
1854aa6d63Sopenharmony_ciimport os
1954aa6d63Sopenharmony_ciimport re
2054aa6d63Sopenharmony_ciimport stat
2154aa6d63Sopenharmony_ciimport sys
2254aa6d63Sopenharmony_cifrom subprocess import Popen
2354aa6d63Sopenharmony_cifrom subprocess import PIPE
2454aa6d63Sopenharmony_ci
2554aa6d63Sopenharmony_ciglobal_config = {}
2654aa6d63Sopenharmony_ci
2754aa6d63Sopenharmony_citemplates = {
2854aa6d63Sopenharmony_ci    'generate-keypair': {
2954aa6d63Sopenharmony_ci        'required': ['keyAlias', 'keyAlg', 'keySize', 'keystoreFile'],
3054aa6d63Sopenharmony_ci        'others': ['keyPwd', 'keystorePwd']
3154aa6d63Sopenharmony_ci    },
3254aa6d63Sopenharmony_ci    'generate-csr': {
3354aa6d63Sopenharmony_ci        'required': ['keyAlias', 'signAlg', 'subject', 'keystoreFile', 'outFile'],
3454aa6d63Sopenharmony_ci        'others': ['keyPwd', 'keystorePwd']
3554aa6d63Sopenharmony_ci    },
3654aa6d63Sopenharmony_ci    'generate-ca': {
3754aa6d63Sopenharmony_ci        'required': ['keyAlias', 'signAlg', 'keyAlg', 'keySize', 'subject', 'keystoreFile', 'outFile'],
3854aa6d63Sopenharmony_ci        'others': ['keyPwd', 'keystorePwd', 'issuer', 'issuerKeyAlias', 'issuerKeyPwd', 'validity',
3954aa6d63Sopenharmony_ci                   'basicConstraintsPathLen']
4054aa6d63Sopenharmony_ci    },
4154aa6d63Sopenharmony_ci    'generate-app-cert': {
4254aa6d63Sopenharmony_ci        'required': ['keyAlias', 'signAlg', 'issuer', 'issuerKeyAlias', 'subject', 'keystoreFile',
4354aa6d63Sopenharmony_ci                     'subCaCertFile', 'rootCaCertFile', 'outForm', 'outFile'],
4454aa6d63Sopenharmony_ci        'others': ['keyPwd', 'keystorePwd', 'issuerKeyPwd', 'validity',
4554aa6d63Sopenharmony_ci                   'basicConstraintsPathLen']
4654aa6d63Sopenharmony_ci    },
4754aa6d63Sopenharmony_ci    'generate-profile-cert': {
4854aa6d63Sopenharmony_ci        'required': ['keyAlias', 'signAlg', 'issuer', 'issuerKeyAlias', 'subject', 'keystoreFile',
4954aa6d63Sopenharmony_ci                     'subCaCertFile', 'rootCaCertFile', 'outForm', 'outFile'],
5054aa6d63Sopenharmony_ci        'others': ['keyPwd', 'keystorePwd', 'issuerKeyPwd', 'validity',
5154aa6d63Sopenharmony_ci                   'basicConstraintsPathLen']
5254aa6d63Sopenharmony_ci    },
5354aa6d63Sopenharmony_ci    'sign-profile': {
5454aa6d63Sopenharmony_ci        'required': ['keyAlias', 'signAlg', 'mode', 'profileCertFile', 'inFile', 'keystoreFile', 'outFile'],
5554aa6d63Sopenharmony_ci        'others': ['keyPwd', 'keystorePwd']
5654aa6d63Sopenharmony_ci    },
5754aa6d63Sopenharmony_ci    'sign-app': {
5854aa6d63Sopenharmony_ci        'required': ['keyAlias', 'signAlg', 'mode', 'appCertFile', 'profileFile', 'inFile', 'keystoreFile', 'outFile'],
5954aa6d63Sopenharmony_ci        'others': ['keyPwd', 'keystorePwd', 'inForm', 'signCode']
6054aa6d63Sopenharmony_ci    },
6154aa6d63Sopenharmony_ci}
6254aa6d63Sopenharmony_ci
6354aa6d63Sopenharmony_ci
6454aa6d63Sopenharmony_cidef print_help():
6554aa6d63Sopenharmony_ci    content = "\n" \
6654aa6d63Sopenharmony_ci              "Usage:python autosign.py <generate|sign> \n" \
6754aa6d63Sopenharmony_ci              "    signtool.jar : Main progress jar file\n" \
6854aa6d63Sopenharmony_ci              "\n" \
6954aa6d63Sopenharmony_ci              "Example: \n" \
7054aa6d63Sopenharmony_ci              "    python autosign.py createAppCertAndProfile \n" \
7154aa6d63Sopenharmony_ci              "    python autosign.py signHap" \
7254aa6d63Sopenharmony_ci              "\n"
7354aa6d63Sopenharmony_ci    print(content)
7454aa6d63Sopenharmony_ci
7554aa6d63Sopenharmony_ci
7654aa6d63Sopenharmony_cidef get_from_single_config(config_key, item_key, required=False):
7754aa6d63Sopenharmony_ci    param = global_config.get(config_key, {}).get(item_key, None)
7854aa6d63Sopenharmony_ci    if not param:
7954aa6d63Sopenharmony_ci        param = global_config.get('common', {}).get(item_key, None)
8054aa6d63Sopenharmony_ci    if not param:
8154aa6d63Sopenharmony_ci        if required:
8254aa6d63Sopenharmony_ci            print('Prepare loading: {}, config: {}'.format(config_key, global_config.get(config_key)))
8354aa6d63Sopenharmony_ci            print("Params {} is required.".format(item_key))
8454aa6d63Sopenharmony_ci            exit(1)
8554aa6d63Sopenharmony_ci    return param
8654aa6d63Sopenharmony_ci
8754aa6d63Sopenharmony_ci
8854aa6d63Sopenharmony_cidef prepare_dir(dir_name):
8954aa6d63Sopenharmony_ci    if not os.path.exists(dir_name):
9054aa6d63Sopenharmony_ci        os.mkdir(dir_name)
9154aa6d63Sopenharmony_ci
9254aa6d63Sopenharmony_ci
9354aa6d63Sopenharmony_cidef load_engine(engine_config):
9454aa6d63Sopenharmony_ci    tar_dir = global_config.get('config', {}).get('targetDir')
9554aa6d63Sopenharmony_ci    prepare_dir(tar_dir)
9654aa6d63Sopenharmony_ci
9754aa6d63Sopenharmony_ci    cmds = []
9854aa6d63Sopenharmony_ci    for eng_k, eng_v in engine_config.items():
9954aa6d63Sopenharmony_ci        template = templates.get(eng_v)
10054aa6d63Sopenharmony_ci        cmd = [eng_v]
10154aa6d63Sopenharmony_ci        for required_key in template.get('required'):
10254aa6d63Sopenharmony_ci            param = get_from_single_config(eng_k, required_key, True)
10354aa6d63Sopenharmony_ci            if required_key.endswith('File') and required_key != 'inFile' and os.path.basename(param) == param:
10454aa6d63Sopenharmony_ci                param = os.path.join(tar_dir, param)
10554aa6d63Sopenharmony_ci            cmd.append('-{}'.format(required_key))
10654aa6d63Sopenharmony_ci            cmd.append(param)
10754aa6d63Sopenharmony_ci
10854aa6d63Sopenharmony_ci        for others_key in template.get('others'):
10954aa6d63Sopenharmony_ci            param = get_from_single_config(eng_k, others_key, False)
11054aa6d63Sopenharmony_ci            if param:
11154aa6d63Sopenharmony_ci                cmd.append('-{}'.format(others_key))
11254aa6d63Sopenharmony_ci                cmd.append(param)
11354aa6d63Sopenharmony_ci        cmds.append(cmd)
11454aa6d63Sopenharmony_ci    return cmds
11554aa6d63Sopenharmony_ci
11654aa6d63Sopenharmony_ci
11754aa6d63Sopenharmony_cidef run_target(cmd):
11854aa6d63Sopenharmony_ci    command = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, shell=False)
11954aa6d63Sopenharmony_ci    out = command.stdout.readlines()
12054aa6d63Sopenharmony_ci    with open("log.txt", mode='a+', encoding='utf-8') as f:
12154aa6d63Sopenharmony_ci        if len(out) > 0:
12254aa6d63Sopenharmony_ci            f.writelines(' '.join(cmd) + "\r\n")
12354aa6d63Sopenharmony_ci        for line in out:
12454aa6d63Sopenharmony_ci            f.writelines(str(line.strip()) + "\r\n")
12554aa6d63Sopenharmony_ci
12654aa6d63Sopenharmony_ci    success = True
12754aa6d63Sopenharmony_ci    error = command.stderr.readlines()
12854aa6d63Sopenharmony_ci    with open("error.txt", mode='a+', encoding='utf-8') as f:
12954aa6d63Sopenharmony_ci        if len(error) > 0:
13054aa6d63Sopenharmony_ci            f.writelines(' '.join(cmd) + "\r\n")
13154aa6d63Sopenharmony_ci
13254aa6d63Sopenharmony_ci        for line in error:
13354aa6d63Sopenharmony_ci            success = False
13454aa6d63Sopenharmony_ci            f.writelines(str(line.strip()) + "\r\n")
13554aa6d63Sopenharmony_ci
13654aa6d63Sopenharmony_ci    command.wait()
13754aa6d63Sopenharmony_ci    return success
13854aa6d63Sopenharmony_ci
13954aa6d63Sopenharmony_ci
14054aa6d63Sopenharmony_cidef run_with_engine(engine, jar):
14154aa6d63Sopenharmony_ci    cmds = load_engine(engine)
14254aa6d63Sopenharmony_ci    for cmd in cmds:
14354aa6d63Sopenharmony_ci        cmd.insert(0, jar)
14454aa6d63Sopenharmony_ci        cmd.insert(0, '-jar')
14554aa6d63Sopenharmony_ci        cmd.insert(0, 'java')
14654aa6d63Sopenharmony_ci        result = run_target(cmd)
14754aa6d63Sopenharmony_ci        if not result:
14854aa6d63Sopenharmony_ci            print("Command error on executing cmd, please check error.txt")
14954aa6d63Sopenharmony_ci            print(' '.join(cmd))
15054aa6d63Sopenharmony_ci            exit(1)
15154aa6d63Sopenharmony_ci    print("Success!")
15254aa6d63Sopenharmony_ci    pass
15354aa6d63Sopenharmony_ci
15454aa6d63Sopenharmony_ci
15554aa6d63Sopenharmony_cidef do_sign(jar):
15654aa6d63Sopenharmony_ci    sign_engine_config = {
15754aa6d63Sopenharmony_ci        'sign.profile': 'sign-profile',
15854aa6d63Sopenharmony_ci        'sign.app': 'sign-app'
15954aa6d63Sopenharmony_ci    }
16054aa6d63Sopenharmony_ci    run_with_engine(sign_engine_config, jar)
16154aa6d63Sopenharmony_ci
16254aa6d63Sopenharmony_ci
16354aa6d63Sopenharmony_cidef do_sign_hap(jar):
16454aa6d63Sopenharmony_ci    sign_hap_engine_config = {
16554aa6d63Sopenharmony_ci        'sign.app': 'sign-app'
16654aa6d63Sopenharmony_ci    }
16754aa6d63Sopenharmony_ci    run_with_engine(sign_hap_engine_config, jar)
16854aa6d63Sopenharmony_ci
16954aa6d63Sopenharmony_ci
17054aa6d63Sopenharmony_cidef do_sign_elf(jar):
17154aa6d63Sopenharmony_ci    sign_elf_engine_config = {
17254aa6d63Sopenharmony_ci        'sign.app': 'sign-app'
17354aa6d63Sopenharmony_ci    }
17454aa6d63Sopenharmony_ci    run_with_engine(sign_elf_engine_config, jar)
17554aa6d63Sopenharmony_ci
17654aa6d63Sopenharmony_ci
17754aa6d63Sopenharmony_cidef do_generate(jar):
17854aa6d63Sopenharmony_ci    cert_engine_config = {
17954aa6d63Sopenharmony_ci        'app.keypair': 'generate-keypair',
18054aa6d63Sopenharmony_ci        'profile.keypair': 'generate-keypair',
18154aa6d63Sopenharmony_ci        'csr': 'generate-csr',
18254aa6d63Sopenharmony_ci        'root-ca': 'generate-ca',
18354aa6d63Sopenharmony_ci        'sub-ca.app': 'generate-ca',
18454aa6d63Sopenharmony_ci        'sub-ca.profile': 'generate-ca',
18554aa6d63Sopenharmony_ci        'cert.app': 'generate-app-cert',
18654aa6d63Sopenharmony_ci        'cert.profile': 'generate-profile-cert',
18754aa6d63Sopenharmony_ci    }
18854aa6d63Sopenharmony_ci    run_with_engine(cert_engine_config, jar)
18954aa6d63Sopenharmony_ci
19054aa6d63Sopenharmony_ci
19154aa6d63Sopenharmony_cidef do_generate_root_cert(jar):
19254aa6d63Sopenharmony_ci    root_engine_config = {
19354aa6d63Sopenharmony_ci        'profile.keypair': 'generate-keypair',
19454aa6d63Sopenharmony_ci        'root-ca': 'generate-ca',
19554aa6d63Sopenharmony_ci        'sub-ca.app': 'generate-ca',
19654aa6d63Sopenharmony_ci        'sub-ca.profile': 'generate-ca',
19754aa6d63Sopenharmony_ci        'cert.profile': 'generate-profile-cert',
19854aa6d63Sopenharmony_ci    }
19954aa6d63Sopenharmony_ci    run_with_engine(root_engine_config, jar)
20054aa6d63Sopenharmony_ci
20154aa6d63Sopenharmony_ci
20254aa6d63Sopenharmony_cidef do_generate_app_cert(jar):
20354aa6d63Sopenharmony_ci    app_cert_engine_config = {
20454aa6d63Sopenharmony_ci        'app.keypair': 'generate-keypair',
20554aa6d63Sopenharmony_ci        'cert.app': 'generate-app-cert',
20654aa6d63Sopenharmony_ci    }
20754aa6d63Sopenharmony_ci    run_with_engine(app_cert_engine_config, jar)
20854aa6d63Sopenharmony_ci
20954aa6d63Sopenharmony_ci
21054aa6d63Sopenharmony_cidef do_sign_profile(jar):
21154aa6d63Sopenharmony_ci    app_cert_engine_config = {
21254aa6d63Sopenharmony_ci        'sign.profile': 'sign-profile',
21354aa6d63Sopenharmony_ci    }
21454aa6d63Sopenharmony_ci    run_with_engine(app_cert_engine_config, jar)
21554aa6d63Sopenharmony_ci
21654aa6d63Sopenharmony_ci
21754aa6d63Sopenharmony_cidef convert_to_map(line, temp_map):
21854aa6d63Sopenharmony_ci    line = line.strip('\n')
21954aa6d63Sopenharmony_ci    strs = line.split('=', 1)
22054aa6d63Sopenharmony_ci
22154aa6d63Sopenharmony_ci    if len(strs) == 2:
22254aa6d63Sopenharmony_ci        if strs[1].startswith('$'):
22354aa6d63Sopenharmony_ci            temp_map[strs[0]] = temp_map[strs[1][1:]]
22454aa6d63Sopenharmony_ci        else:
22554aa6d63Sopenharmony_ci            temp_map[strs[0]] = strs[1]
22654aa6d63Sopenharmony_ci
22754aa6d63Sopenharmony_ci
22854aa6d63Sopenharmony_cidef load_config(config):
22954aa6d63Sopenharmony_ci    config_file = config
23054aa6d63Sopenharmony_ci    temp_map = {}
23154aa6d63Sopenharmony_ci    with open(config_file, 'r', encoding='utf-8') as f:
23254aa6d63Sopenharmony_ci        for line in f.readlines():
23354aa6d63Sopenharmony_ci            if not re.match(r'\s*//[\s\S]*', line):
23454aa6d63Sopenharmony_ci                convert_to_map(line, temp_map)
23554aa6d63Sopenharmony_ci
23654aa6d63Sopenharmony_ci    for mk, mv in temp_map.items():
23754aa6d63Sopenharmony_ci        strs = mk.rsplit('.', 1)
23854aa6d63Sopenharmony_ci        if not global_config.get(strs[0]):
23954aa6d63Sopenharmony_ci            global_config[strs[0]] = {}
24054aa6d63Sopenharmony_ci        global_config[strs[0]][strs[-1]] = mv
24154aa6d63Sopenharmony_ci
24254aa6d63Sopenharmony_ci
24354aa6d63Sopenharmony_cidef process_cmd():
24454aa6d63Sopenharmony_ci    args = sys.argv
24554aa6d63Sopenharmony_ci    if len(args) <= 1 or '--help' == args[1] or '-h' == args[1]:
24654aa6d63Sopenharmony_ci        print_help()
24754aa6d63Sopenharmony_ci        exit(0)
24854aa6d63Sopenharmony_ci
24954aa6d63Sopenharmony_ci    action = args[1]
25054aa6d63Sopenharmony_ci    if action not in ['createRootAndSubCert', 'createAppCertAndProfile', 'signHap', 'signElf']:
25154aa6d63Sopenharmony_ci        print("Not support cmd")
25254aa6d63Sopenharmony_ci        print_help()
25354aa6d63Sopenharmony_ci        exit(1)
25454aa6d63Sopenharmony_ci    return action
25554aa6d63Sopenharmony_ci
25654aa6d63Sopenharmony_ci
25754aa6d63Sopenharmony_cidef process_jar():
25854aa6d63Sopenharmony_ci    read_jar_file = global_config.get('config', {}).get('signtool')
25954aa6d63Sopenharmony_ci    if not os.path.exists(read_jar_file):
26054aa6d63Sopenharmony_ci        print("Jar file '{}' not found".format(read_jar_file))
26154aa6d63Sopenharmony_ci        exit(1)
26254aa6d63Sopenharmony_ci    return read_jar_file
26354aa6d63Sopenharmony_ci
26454aa6d63Sopenharmony_ci
26554aa6d63Sopenharmony_cidef replace_cert_in_profile():
26654aa6d63Sopenharmony_ci    profile_file = global_config.get('sign.profile', {}).get('inFile')
26754aa6d63Sopenharmony_ci    app_cert_file = global_config.get('cert.app', {}).get('outFile')
26854aa6d63Sopenharmony_ci    tar_dir = global_config.get('config', {}).get('targetDir')
26954aa6d63Sopenharmony_ci    app_cert_file = os.path.join(tar_dir, app_cert_file)
27054aa6d63Sopenharmony_ci    if not os.path.exists(profile_file):
27154aa6d63Sopenharmony_ci        print("profile file '{}' not found".format(jar_file))
27254aa6d63Sopenharmony_ci        exit(1)
27354aa6d63Sopenharmony_ci    if not os.path.exists(app_cert_file):
27454aa6d63Sopenharmony_ci        print("app cert file '{}' not found".format(jar_file))
27554aa6d63Sopenharmony_ci        exit(1)
27654aa6d63Sopenharmony_ci
27754aa6d63Sopenharmony_ci    app_cert = ''
27854aa6d63Sopenharmony_ci    # read app cert
27954aa6d63Sopenharmony_ci    with open(app_cert_file, 'r', encoding='utf-8') as f:
28054aa6d63Sopenharmony_ci        app_cert_temp = f.read()
28154aa6d63Sopenharmony_ci        app_cert = app_cert_temp.split("-----END CERTIFICATE-----")[0] + "-----END CERTIFICATE-----\n"
28254aa6d63Sopenharmony_ci
28354aa6d63Sopenharmony_ci    profile = {}
28454aa6d63Sopenharmony_ci    # read profile
28554aa6d63Sopenharmony_ci    with open(profile_file, 'r', encoding='utf-8') as f:
28654aa6d63Sopenharmony_ci        profile = json.load(f)
28754aa6d63Sopenharmony_ci
28854aa6d63Sopenharmony_ci    try:
28954aa6d63Sopenharmony_ci        profile["bundle-info"]["distribution-certificate"] = app_cert
29054aa6d63Sopenharmony_ci    except KeyError:
29154aa6d63Sopenharmony_ci        print("could not find distribution-certificate key in profile")
29254aa6d63Sopenharmony_ci
29354aa6d63Sopenharmony_ci    # save profile
29454aa6d63Sopenharmony_ci    flags = os.O_WRONLY | os.O_TRUNC
29554aa6d63Sopenharmony_ci    modes = stat.S_IWUSR
29654aa6d63Sopenharmony_ci    with os.fdopen(os.open(profile_file, flags, modes), 'w') as profile_write:
29754aa6d63Sopenharmony_ci        json.dump(profile, profile_write)
29854aa6d63Sopenharmony_ci
29954aa6d63Sopenharmony_ci
30054aa6d63Sopenharmony_ciif __name__ == '__main__':
30154aa6d63Sopenharmony_ci    act = process_cmd()
30254aa6d63Sopenharmony_ci    if act == 'createRootAndSubCert':
30354aa6d63Sopenharmony_ci        load_config('createRootAndSubCert.config')
30454aa6d63Sopenharmony_ci        jar_file = process_jar()
30554aa6d63Sopenharmony_ci        do_generate_root_cert(jar_file)
30654aa6d63Sopenharmony_ci    elif act == 'createAppCertAndProfile':
30754aa6d63Sopenharmony_ci        load_config('createAppCertAndProfile.config')
30854aa6d63Sopenharmony_ci        jar_file = process_jar()
30954aa6d63Sopenharmony_ci        do_generate_app_cert(jar_file)
31054aa6d63Sopenharmony_ci        replace_cert_in_profile()
31154aa6d63Sopenharmony_ci        do_sign_profile(jar_file)
31254aa6d63Sopenharmony_ci    elif act == 'signHap':
31354aa6d63Sopenharmony_ci        load_config('signHap.config')
31454aa6d63Sopenharmony_ci        jar_file = process_jar()
31554aa6d63Sopenharmony_ci        do_sign_hap(jar_file)
31654aa6d63Sopenharmony_ci    elif act == 'signElf':
31754aa6d63Sopenharmony_ci        load_config('signElf.config')
31854aa6d63Sopenharmony_ci        jar_file = process_jar()
31954aa6d63Sopenharmony_ci        do_sign_elf(jar_file)
32054aa6d63Sopenharmony_ci