CryptoPkg/openssl: add openssl3 configure scripts

Rewrite the script to configure openssl 3.0 from scratch.  It's two
scripts now:

 * Tiny helper script, dumping the perl configdata as json.
 * Actual configure.py script, written in python, which copies over
   the generated files to openssl-gen and updates the OpensslLib*.inf
   file lists and build flags.

The configuration workflow has changed a bit:

 * All generated files are stored in the OpensslGen directory tree.
 * For ec/no-ec builds two different header files are used.  Default is
   the ec variant, and the new EDK2_OPENSSL_NOEC define is used to
   select the no-ec build.  A five line wrapper include is used to pick
   the one or the other.
 * For non-accel builds -DOPENSSL_NO_ASM on the command line is used
   (same as before).
 * For configration defines the OPENSSL_FLAGS_$(variant) variable is
   used, where variant is the architecture for the accelerated builds
   and 'NOASM' for the non-accelerated builds.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Yi Li <yi1.li@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Xiaoyu Lu <xiaoyu1.lu@intel.com>
Cc: Guomin Jiang <guomin.jiang@intel.com>
Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Brian J. Johnson <brian.johnson@hpe.com>
Tested-by: Kenneth Lautner <klautner@microsoft.com>
This commit is contained in:
Gerd Hoffmann 2023-08-03 12:37:22 +08:00 committed by mergify[bot]
parent 7cede6d5f4
commit 81f5aa0700
3 changed files with 416 additions and 2 deletions

View File

@ -28,9 +28,9 @@ on the cryptography.
refer to edk2/Readme.md for how to clone the code.
=============================================================================
About process_files.pl
About configure.py
=============================================================================
"process_files.pl" is one Perl script which runs the OpenSSL Configure,
"configure.py" is one python script which runs the OpenSSL Configure,
then processes the resulting file list into our local OpensslLib.inf and
OpensslLibCrypto.inf.
This only needs to be done once by the maintainer / developer when

View File

@ -0,0 +1,395 @@
#!/usr/bin/python3
# SPDX-License-Identifier: BSD-2-Clause-Patent
import os
import sys
import json
import shutil
import pprint
import argparse
import subprocess
def openssl_configure(openssldir, target, ec = True):
""" Run openssl Configure script. """
cmdline = [
'perl',
'Configure',
'--config=../UefiAsm.conf',
'--api=1.1.1',
'--with-rand-seed=none',
target,
'no-afalgeng',
'no-aria',
'no-async',
'no-autoerrinit',
'no-autoload-config',
'no-bf',
'no-blake2',
'no-camellia',
'no-capieng',
'no-cast',
'no-chacha',
'no-cmac',
'no-cmp',
'no-cms',
'no-ct',
'no-deprecated',
'no-des',
'no-dgram',
'no-dsa',
'no-dso',
'no-dtls',
'no-dtls1-method',
'no-dtls1_2-method',
'no-dynamic-engine',
'no-ec2m',
'no-engine',
'no-err',
'no-filenames',
'no-gost',
'no-hw',
'no-idea',
'no-ktls',
'no-makedepend',
'no-module',
'no-md4',
'no-mdc2',
'no-multiblock',
'no-nextprotoneg',
'no-pic',
'no-psk',
'no-ocb',
'no-ocsp',
'no-padlockeng',
'no-poly1305',
'no-posix-io',
'no-rc2',
'no-rc4',
'no-rc5',
'no-rfc3779',
'no-rmd160',
'no-scrypt',
'no-seed',
'no-shared',
'no-siphash',
'no-siv',
'no-sm2',
'no-sm4',
'no-sock',
'no-srp',
'no-srtp',
'no-ssl',
'no-ssl3-method',
'no-ssl-trace',
'no-static-engine',
'no-stdio',
'no-threads',
'no-tls1_3',
'no-ts',
'no-ui-console',
'no-whirlpool',
'disable-legacy',
]
if not ec:
cmdline += [ 'no-ec', ]
print('')
print(f'# -*- configure openssl for {target} (ec={ec}) -*-')
rc = subprocess.run(cmdline, cwd = openssldir,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE)
if rc.returncode:
print(rc.stdout)
print(rc.stderr)
sys.exit(rc.returncode)
def openssl_run_make(openssldir, target):
"""
Run make utility to generate files or cleanup.
Target can be either a string or a list of strings.
"""
cmdline = [ 'make', '--silent' ]
if isinstance(target, list):
cmdline += target
else:
cmdline += [ target, ]
rc = subprocess.run(cmdline, cwd = openssldir)
rc.check_returncode()
def get_configdata(openssldir):
"""
Slurp openssl config data as JSON,
using a little perl helper script.
"""
cmdline = [
'perl',
'perl2json.pl',
openssldir,
]
rc = subprocess.run(cmdline, stdout = subprocess.PIPE)
rc.check_returncode()
return json.loads(rc.stdout)
def is_asm(filename):
""" Check whenevr the passed file is an assembler file """
if filename.endswith('.s') or filename.endswith('.S'):
return True
return False
def copy_generated_file(src, dst):
src_file = []
with open(src, 'r') as fsrc:
src_file = fsrc.readlines()
with open(dst, 'w') as fdst:
for lines in range(len(src_file)):
s = src_file[lines]
s = s.rstrip() + "\r\n"
fdst.write(s.expandtabs())
def generate_files(openssldir, opensslgendir, asm, filelist):
"""
Generate files, using make, and copy over the results to the
directory tree for generated openssl files. Creates
subdirectories as needed.
"""
openssl_run_make(openssldir, filelist)
for filename in filelist:
src = os.path.join(openssldir, filename)
if is_asm(filename):
""" rename MSFT asm files to .nasm """
if 'IA32-MSFT' in asm:
filename = filename.replace('.S', '.nasm')
elif 'X64-MSFT' in asm:
filename = filename.replace('.s', '.nasm')
dst = os.path.join(opensslgendir, asm, filename)
else:
dst = os.path.join(opensslgendir, filename)
os.makedirs(os.path.dirname(dst), exist_ok = True)
copy_generated_file(src, dst)
def generate_include_files(openssldir, opensslgendir, asm, cfg):
""" Generate openssl include files """
print('# generate include files')
filelist = cfg['unified_info']['generate'].keys()
filelist = list(filter(lambda f: 'include' in f, filelist))
generate_files(openssldir, opensslgendir, asm, filelist)
def generate_library_files(openssldir, opensslgendir, asm, cfg, obj):
"""
Generate openssl source files for a given library. Handles
mostly assembler files, but a few C sources are generated too.
"""
filelist = get_source_list(cfg, obj, True)
if filelist:
print(f'# generate source files for {obj}')
generate_files(openssldir, opensslgendir, asm, filelist)
def generate_all_files(openssldir, opensslgendir, asm, cfg):
""" Generate all files needed. """
generate_include_files(openssldir, opensslgendir, asm, cfg)
generate_library_files(openssldir, opensslgendir, asm, cfg, 'libcrypto')
generate_library_files(openssldir, opensslgendir, asm, cfg, 'providers/libcommon.a')
generate_library_files(openssldir, opensslgendir, asm, cfg, 'libssl')
def get_source_list(cfg, obj, gen):
"""
Gets the list of source files needed to create a specific object.
* If 'gen' is True the function returns the list of generated
files.
* If 'gen' is False the function returns the list of files not
generated (which are used from the submodule directly).
Note: Will call itself recursively to resolve nested dependencies.
"""
sources = cfg['unified_info']['sources']
generate = cfg['unified_info']['generate']
srclist = []
if sources.get(obj):
for item in sources.get(obj):
srclist += get_source_list(cfg, item, gen)
else:
is_generated = generate.get(obj) is not None
if is_generated == gen:
srclist += [ obj, ]
return srclist
def get_sources(cfg, obj, asm):
"""
Get the list of all sources files. Will fetch both generated
and not generated file lists and update the paths accordingly, so
the openssl submodule or the sub-tree for generated files is
referenced as needed.
"""
srclist = get_source_list(cfg, obj, False)
genlist = get_source_list(cfg, obj, True)
srclist = list(map(lambda x: f'$(OPENSSL_PATH)/{x}', srclist))
c_list = list(map(lambda x: f'$(OPENSSL_GEN_PATH)/{x}',
filter(lambda x: not is_asm(x), genlist)))
asm_list = list(map(lambda x: f'$(OPENSSL_GEN_PATH)/{asm}/{x}',
filter(is_asm, genlist)))
return srclist + c_list + asm_list
def sources_filter_fn(filename):
"""
Filter source lists. Drops files we don't want include or
need replace with our own uefi-specific version.
"""
exclude = [
'randfile.c',
'/store/',
'/storemgmt/',
'/encode_decode/encode',
'/pkcs12/',
'statem_srvr.c',
'extensions_srvr.c',
'defltprov.c',
'baseprov.c',
'provider_predefined.c',
]
for item in exclude:
if item in filename:
return False
return True
def libcrypto_sources(cfg, asm = None):
""" Get source file list for libcrypto """
files = get_sources(cfg, 'libcrypto', asm)
files += get_sources(cfg, 'providers/libcommon.a', asm)
files = list(filter(sources_filter_fn, files))
return files
def libssl_sources(cfg, asm = None):
""" Get source file list for libssl """
files = get_sources(cfg, 'libssl', asm)
files = list(filter(sources_filter_fn, files))
return files
def update_inf(filename, sources, arch = None, defines = []):
"""
Update inf file, replace source file list and build flags.
"""
head = ''
tail = ''
state = 0
if arch:
section = f'Sources.{arch}'
flags = f'OPENSSL_FLAGS_{arch}'
else:
section = None
flags = f'OPENSSL_FLAGS_NOASM'
state = 1
# read and parse file
with open(filename, 'r') as f:
while True:
line = f.readline()
if line == '':
break
if state in [0, 1]:
if flags in line:
(keep, replace) = line.split('=')
args = map(lambda x: f'-D{x}', defines)
head += keep + '= ' + ' '.join(args)
head = head.rstrip() + '\r\n'
else:
head += line.rstrip() + '\r\n'
if state == 0 and section in line:
state = 1
if state == 1 and 'Autogenerated files list starts here' in line:
state = 2
if state == 2 and 'Autogenerated files list ends here' in line:
state = 3
if state == 3:
tail += line.rstrip() + '\r\n'
# write updated file
with open(filename, 'w') as f:
f.write(head)
for src in sources:
f.write(f' {src}\r\n')
f.write(tail)
def update_MSFT_asm_format(asm, filelist):
""" rename MSFT asm files to .nasm """
if 'IA32-MSFT' in asm:
for file_index in range(len(filelist)):
filelist[file_index] = filelist[file_index].replace('.S', '.nasm')
elif 'X64-MSFT' in asm:
for file_index in range(len(filelist)):
filelist[file_index] = filelist[file_index].replace('.s', '.nasm')
def main():
# prepare
os.chdir(os.path.dirname(os.path.abspath(__file__)))
openssldir = os.path.join(os.getcwd(), 'openssl')
opensslgendir = os.path.join(os.getcwd(), 'OpensslGen')
# asm accel configs (see UefiAsm.conf)
for ec in [True, False]:
if ec:
inf = 'OpensslLibFullAccel.inf'
hdr = 'configuration-ec.h'
else:
inf = 'OpensslLibAccel.inf'
hdr = 'configuration-noec.h'
sources = {}
defines = {}
for asm in [ 'UEFI-IA32-MSFT', 'UEFI-IA32-GCC',
'UEFI-X64-MSFT', 'UEFI-X64-GCC']:
(uefi, arch, cc) = asm.split('-')
archcc = f'{arch}-{cc}'
openssl_configure(openssldir, asm, ec = ec);
cfg = get_configdata(openssldir)
generate_all_files(openssldir, opensslgendir, archcc, cfg)
shutil.move(os.path.join(opensslgendir, 'include', 'openssl', 'configuration.h'),
os.path.join(opensslgendir, 'include', 'openssl', hdr))
openssl_run_make(openssldir, 'distclean')
srclist = libcrypto_sources(cfg, archcc) + libssl_sources(cfg, archcc)
sources[archcc] = list(map(lambda x: f'{x} | {cc}', filter(is_asm, srclist)))
update_MSFT_asm_format(archcc, sources[archcc])
sources[arch] = list(filter(lambda x: not is_asm(x), srclist))
defines[arch] = cfg['unified_info']['defines']['libcrypto']
ia32accel = sources['IA32'] + sources['IA32-MSFT'] + sources['IA32-GCC']
x64accel = sources['X64'] + sources['X64-MSFT'] + sources['X64-GCC']
update_inf(inf, ia32accel, 'IA32', defines['IA32'])
update_inf(inf, x64accel, 'X64', defines['X64'])
# noaccel - ec enabled
openssl_configure(openssldir, 'UEFI', ec = True);
cfg = get_configdata(openssldir)
generate_all_files(openssldir, opensslgendir, None, cfg)
openssl_run_make(openssldir, 'distclean')
defines = []
if 'libcrypto' in cfg['unified_info']['defines']:
defines = cfg['unified_info']['defines']['libcrypto']
update_inf('OpensslLibFull.inf',
libcrypto_sources(cfg) + libssl_sources(cfg),
defines)
# noaccel - ec disabled
openssl_configure(openssldir, 'UEFI', ec = False);
cfg = get_configdata(openssldir)
generate_all_files(openssldir, opensslgendir, None, cfg)
openssl_run_make(openssldir, 'distclean')
update_inf('OpensslLibCrypto.inf',
libcrypto_sources(cfg),
None, defines)
update_inf('OpensslLib.inf',
libcrypto_sources(cfg) + libssl_sources(cfg),
None, defines)
# wrap header file
confighdr = os.path.join(opensslgendir, 'include', 'openssl', 'configuration.h')
with open(confighdr, 'w') as f:
f.write('#ifdef EDK2_OPENSSL_NOEC\r\n'
'# include "configuration-noec.h"\r\n'
'#else\r\n'
'# include "configuration-ec.h"\r\n'
'#endif\r\n')
if __name__ == '__main__':
sys.exit(main())

View File

@ -0,0 +1,19 @@
#!/usr/bin/perl
#
# write out configdata.pm as json
#
use strict;
use warnings;
use JSON;
BEGIN {
my $openssldir = shift;
push @INC, $openssldir;
}
use configdata qw/%config %target %unified_info/;
my %data;
$data{'config'} = \%config;
$data{'target'} = \%target;
$data{'unified_info'} = \%unified_info;
print encode_json(\%data)