|
|
d27964f |
# -*- coding: utf-8 -*-
|
|
|
d27964f |
#
|
|
|
d27964f |
# Generate OpenSSL configuration file for AusweisApp2 from settings found
|
|
|
d27964f |
# in the application's 'config.json' file.
|
|
|
d27964f |
#
|
|
|
d27964f |
# Copyright (c) 2020 Björn Esser <besser82@fedoraproject.org>
|
|
|
d27964f |
#
|
|
|
d27964f |
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
d27964f |
# of this software and associated documentation files (the "Software"), to deal
|
|
|
d27964f |
# in the Software without restriction, including without limitation the rights
|
|
|
d27964f |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
d27964f |
# copies of the Software, and to permit persons to whom the Software is
|
|
|
d27964f |
# furnished to do so, subject to the following conditions:
|
|
|
d27964f |
#
|
|
|
d27964f |
# The above copyright notice and this permission notice shall be included in all
|
|
|
d27964f |
# copies or substantial portions of the Software.
|
|
|
d27964f |
#
|
|
|
d27964f |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
d27964f |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
d27964f |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
d27964f |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
d27964f |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
d27964f |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
d27964f |
# SOFTWARE.
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
import json, sys
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
def constant(f):
|
|
|
d27964f |
def fset(self, value):
|
|
|
d27964f |
raise TypeError
|
|
|
d27964f |
def fget(self):
|
|
|
d27964f |
return f()
|
|
|
d27964f |
return property(fget, fset)
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
class _Const(object):
|
|
|
d27964f |
@constant
|
|
|
d27964f |
def CONF_OPTIONS():
|
|
|
d27964f |
return [
|
|
|
d27964f |
'ciphers',
|
|
|
d27964f |
'ellipticCurves',
|
|
|
d27964f |
'signatureAlgorithms',
|
|
|
d27964f |
]
|
|
|
d27964f |
|
|
|
d27964f |
@constant
|
|
|
d27964f |
def CONF_SECTIONS():
|
|
|
d27964f |
return [
|
|
|
d27964f |
'tlsSettings',
|
|
|
d27964f |
'tlsSettingsPsk',
|
|
|
d27964f |
'tlsSettingsRemoteReader',
|
|
|
d27964f |
'tlsSettingsRemoteReaderPairing',
|
|
|
d27964f |
]
|
|
|
d27964f |
|
|
|
d27964f |
@constant
|
|
|
d27964f |
def DEFAULT_CIPHERS_TLS13():
|
|
|
d27964f |
return [
|
|
|
d27964f |
'TLS_AES_256_GCM_SHA384',
|
|
|
d27964f |
'TLS_AES_128_GCM_SHA256',
|
|
|
d27964f |
]
|
|
|
d27964f |
|
|
|
d27964f |
@constant
|
|
|
d27964f |
def KEYSIZE_EC_OPTION():
|
|
|
d27964f |
return 'Ec'
|
|
|
d27964f |
|
|
|
d27964f |
@constant
|
|
|
d27964f |
def KEYSIZE_OPTIONS():
|
|
|
d27964f |
return [
|
|
|
d27964f |
'Rsa',
|
|
|
d27964f |
'Dsa',
|
|
|
d27964f |
'Dh',
|
|
|
d27964f |
]
|
|
|
d27964f |
|
|
|
d27964f |
@constant
|
|
|
d27964f |
def KEYSIZE_SECTIONS():
|
|
|
d27964f |
return [
|
|
|
d27964f |
'minStaticKeySizes',
|
|
|
d27964f |
'minEphemeralKeySizes',
|
|
|
d27964f |
]
|
|
|
d27964f |
|
|
|
d27964f |
@constant
|
|
|
d27964f |
def TLS_VERSIONS():
|
|
|
d27964f |
return {
|
|
|
d27964f |
'TlsV1_2': (2, 'TLSv1.2'),
|
|
|
d27964f |
'TlsV1_3': (3, 'TLSv1.3'),
|
|
|
d27964f |
}
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
CONST = _Const()
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
def get_min_ssl_sec_level(json_data):
|
|
|
d27964f |
sec_level = 0
|
|
|
d27964f |
min_keysize = sys.maxsize
|
|
|
d27964f |
min_ecsize = sys.maxsize
|
|
|
d27964f |
for section in CONST.KEYSIZE_SECTIONS:
|
|
|
d27964f |
if section in json_data:
|
|
|
d27964f |
for option in CONST.KEYSIZE_OPTIONS:
|
|
|
d27964f |
if option in json_data[section]:
|
|
|
d27964f |
if min_keysize > json_data[section][option]:
|
|
|
d27964f |
min_keysize = json_data[section][option]
|
|
|
d27964f |
if CONST.KEYSIZE_EC_OPTION in json_data[section]:
|
|
|
d27964f |
if min_ecsize > json_data[section][CONST.KEYSIZE_EC_OPTION]:
|
|
|
d27964f |
min_ecsize = json_data[section][CONST.KEYSIZE_EC_OPTION]
|
|
|
d27964f |
|
|
|
d27964f |
if min_keysize >= 1000 and min_ecsize >= 160:
|
|
|
d27964f |
sec_level = 1
|
|
|
d27964f |
if min_keysize >= 2000 and min_ecsize >= 224:
|
|
|
d27964f |
sec_level = 2
|
|
|
d27964f |
if min_keysize >= 3000 and min_ecsize >= 256:
|
|
|
d27964f |
sec_level = 3
|
|
|
d27964f |
if min_keysize >= 7000 and min_ecsize >= 384:
|
|
|
d27964f |
sec_level = 4
|
|
|
d27964f |
if min_keysize >= 15000 and min_ecsize >= 512:
|
|
|
d27964f |
sec_level = 5
|
|
|
d27964f |
|
|
|
d27964f |
return sec_level
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
def get_proto_ver(json_data):
|
|
|
d27964f |
conf_dict = {
|
|
|
d27964f |
'minProtocolVersion': list(CONST.TLS_VERSIONS.keys())[-1],
|
|
|
d27964f |
'maxProtocolVersion': list(CONST.TLS_VERSIONS.keys())[0],
|
|
|
d27964f |
}
|
|
|
d27964f |
for section in CONST.CONF_SECTIONS:
|
|
|
d27964f |
if section in json_data:
|
|
|
d27964f |
if 'protocolVersion' in json_data[section]:
|
|
|
d27964f |
have = conf_dict['minProtocolVersion']
|
|
|
d27964f |
want = json_data[section]['protocolVersion']
|
|
|
d27964f |
if CONST.TLS_VERSIONS[want][0] < CONST.TLS_VERSIONS[have][0]:
|
|
|
d27964f |
conf_dict['minProtocolVersion'] = want
|
|
|
d27964f |
have = conf_dict['maxProtocolVersion']
|
|
|
d27964f |
if CONST.TLS_VERSIONS[want][0] > CONST.TLS_VERSIONS[have][0]:
|
|
|
d27964f |
conf_dict['maxProtocolVersion'] = want
|
|
|
d27964f |
|
|
|
d27964f |
return conf_dict
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
def get_ssl_cipher_config(json_data):
|
|
|
d27964f |
conf_dict = dict.fromkeys(CONST.CONF_OPTIONS)
|
|
|
d27964f |
for option in CONST.CONF_OPTIONS:
|
|
|
d27964f |
conf_dict[option] = list()
|
|
|
d27964f |
for section in CONST.CONF_SECTIONS:
|
|
|
d27964f |
if section in json_data:
|
|
|
d27964f |
for option in CONST.CONF_OPTIONS:
|
|
|
d27964f |
if option in json_data[section]:
|
|
|
d27964f |
for value in json_data[section][option]:
|
|
|
d27964f |
if option == 'ciphers' and value.startswith('TLS_'):
|
|
|
d27964f |
if not 'ciphers_tls13' in conf_dict:
|
|
|
d27964f |
conf_dict['ciphers_tls13'] = list()
|
|
|
d27964f |
if not value in conf_dict['ciphers_tls13']:
|
|
|
d27964f |
conf_dict['ciphers_tls13'].append(value)
|
|
|
d27964f |
else:
|
|
|
d27964f |
if not value in conf_dict[option]:
|
|
|
d27964f |
conf_dict[option].append(value)
|
|
|
d27964f |
|
|
|
d27964f |
return conf_dict
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
def print_config_file(conf_dict, sec_level):
|
|
|
d27964f |
max_tls_proto = CONST.TLS_VERSIONS[conf_dict['maxProtocolVersion']][0]
|
|
|
d27964f |
prelude = (
|
|
|
d27964f |
'# This application specific OpenSSL configuration enables all cipher',
|
|
|
d27964f |
'# algorithms, elliptic curves, and signature algorithms, which are',
|
|
|
d27964f |
'# needed for AusweisApp2 to provide full functionality to the end-user.',
|
|
|
d27964f |
'# The order of the algorithms in the list is of no importance, as the',
|
|
|
d27964f |
'# application chooses the algorithm used for a connection from a preset',
|
|
|
d27964f |
'# list, that is ordered in descending preference. This configuration',
|
|
|
d27964f |
'# also limits the minimum and maximum cryptographic protocol versions',
|
|
|
d27964f |
'# to a range needed by AusweisApp2. Additionally FIPS mode is enforced.',
|
|
|
d27964f |
'# The settings used to generate this file have been taken from the',
|
|
|
d27964f |
'# \'config.json\' file, which can be found in the same directory as this',
|
|
|
d27964f |
'# configuration file.',
|
|
|
d27964f |
'',
|
|
|
d27964f |
'openssl_conf = AusweisApp2_conf',
|
|
|
d27964f |
'',
|
|
|
d27964f |
'[AusweisApp2_conf]',
|
|
|
d27964f |
'ssl_conf = AusweisApp2_OpenSSL',
|
|
|
d27964f |
'',
|
|
|
d27964f |
'[AusweisApp2_OpenSSL]',
|
|
|
d27964f |
'alg_section = AusweisApp2_evp',
|
|
|
d27964f |
'system_default = AusweisApp2_ciphers',
|
|
|
d27964f |
'',
|
|
|
d27964f |
'[AusweisApp2_evp]',
|
|
|
d27964f |
'fips_mode = yes',
|
|
|
d27964f |
'',
|
|
|
d27964f |
'[AusweisApp2_ciphers]',
|
|
|
d27964f |
)
|
|
|
d27964f |
print('%s' % '\n'.join(prelude))
|
|
|
d27964f |
print('MinProtocol = %s' % (CONST.TLS_VERSIONS[conf_dict['minProtocolVersion']][1]))
|
|
|
d27964f |
print('MaxProtocol = %s' % (CONST.TLS_VERSIONS[conf_dict['maxProtocolVersion']][1]))
|
|
|
d27964f |
if max_tls_proto >= CONST.TLS_VERSIONS['TlsV1_3'][0]:
|
|
|
d27964f |
if 'ciphers_tls13' in conf_dict:
|
|
|
d27964f |
print('Cipherlist = %s' % (':'.join(conf_dict['ciphers_tls13'])))
|
|
|
d27964f |
else:
|
|
|
d27964f |
print('Cipherlist = %s' % (':'.join(CONST.DEFAULT_CIPHERS_TLS13)))
|
|
|
d27964f |
print('CipherString = @SECLEVEL=%d:%s' % (sec_level, ':'.join(conf_dict['ciphers'])))
|
|
|
d27964f |
print('Curves = %s' % (':'.join(conf_dict['ellipticCurves'])))
|
|
|
d27964f |
print('SignatureAlgorithms = %s' % (':'.join(conf_dict['signatureAlgorithms'])))
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
def main():
|
|
|
d27964f |
if not len(sys.argv) == 2:
|
|
|
d27964f |
sys.exit('Usage: %s <path_to_config.json>' % sys.argv[0])
|
|
|
d27964f |
|
|
|
d27964f |
with open(sys.argv[1], 'r') as conf_file:
|
|
|
d27964f |
conf = json.load(conf_file)
|
|
|
d27964f |
|
|
|
d27964f |
ssl_conf = get_proto_ver(conf)
|
|
|
d27964f |
ssl_conf.update(get_ssl_cipher_config(conf))
|
|
|
d27964f |
|
|
|
d27964f |
print_config_file(ssl_conf, get_min_ssl_sec_level(conf))
|
|
|
d27964f |
|
|
|
d27964f |
|
|
|
d27964f |
if __name__ == '__main__':
|
|
|
d27964f |
main()
|