1#!/usr/bin/env python3 2 3# translate_ciphers.py 4# 5# Copyright The Mbed TLS Contributors 6# SPDX-License-Identifier: Apache-2.0 7# 8# Licensed under the Apache License, Version 2.0 (the "License"); you may 9# not use this file except in compliance with the License. 10# You may obtain a copy of the License at 11# 12# http://www.apache.org/licenses/LICENSE-2.0 13# 14# Unless required by applicable law or agreed to in writing, software 15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17# See the License for the specific language governing permissions and 18# limitations under the License. 19 20""" 21Translate ciphersuite names in Mbed TLS format to OpenSSL and GNUTLS 22standards. 23 24To test the translation functions run: 25python3 -m unittest translate_cipher.py 26""" 27 28import re 29import argparse 30import unittest 31 32class TestTranslateCiphers(unittest.TestCase): 33 """ 34 Ensure translate_ciphers.py translates and formats ciphersuite names 35 correctly 36 """ 37 def test_translate_all_cipher_names(self): 38 """ 39 Translate MbedTLS ciphersuite names to their OpenSSL and 40 GnuTLS counterpart. Use only a small subset of ciphers 41 that exercise each step of the translate functions 42 """ 43 ciphers = [ 44 ("TLS-ECDHE-ECDSA-WITH-NULL-SHA", 45 "+ECDHE-ECDSA:+NULL:+SHA1", 46 "ECDHE-ECDSA-NULL-SHA"), 47 ("TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", 48 "+ECDHE-ECDSA:+AES-128-GCM:+AEAD", 49 "ECDHE-ECDSA-AES128-GCM-SHA256"), 50 ("TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", 51 "+DHE-RSA:+3DES-CBC:+SHA1", 52 "EDH-RSA-DES-CBC3-SHA"), 53 ("TLS-RSA-WITH-AES-256-CBC-SHA", 54 "+RSA:+AES-256-CBC:+SHA1", 55 "AES256-SHA"), 56 ("TLS-PSK-WITH-3DES-EDE-CBC-SHA", 57 "+PSK:+3DES-CBC:+SHA1", 58 "PSK-3DES-EDE-CBC-SHA"), 59 ("TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", 60 None, 61 "ECDHE-ECDSA-CHACHA20-POLY1305"), 62 ("TLS-ECDHE-ECDSA-WITH-AES-128-CCM", 63 "+ECDHE-ECDSA:+AES-128-CCM:+AEAD", 64 None), 65 ("TLS-ECDHE-RSA-WITH-ARIA-256-GCM-SHA384", 66 None, 67 "ECDHE-ARIA256-GCM-SHA384"), 68 ] 69 70 for m, g_exp, o_exp in ciphers: 71 72 if g_exp is not None: 73 g = translate_gnutls(m) 74 self.assertEqual(g, g_exp) 75 76 if o_exp is not None: 77 o = translate_ossl(m) 78 self.assertEqual(o, o_exp) 79 80def translate_gnutls(m_cipher): 81 """ 82 Translate m_cipher from Mbed TLS ciphersuite naming convention 83 and return the GnuTLS naming convention 84 """ 85 86 m_cipher = re.sub(r'\ATLS-', '+', m_cipher) 87 m_cipher = m_cipher.replace("-WITH-", ":+") 88 m_cipher = m_cipher.replace("-EDE", "") 89 90 # SHA in Mbed TLS == SHA1 GnuTLS, 91 # if the last 3 chars are SHA append 1 92 if m_cipher[-3:] == "SHA": 93 m_cipher = m_cipher+"1" 94 95 # CCM or CCM-8 should be followed by ":+AEAD" 96 # Replace "GCM:+SHAxyz" with "GCM:+AEAD" 97 if "CCM" in m_cipher or "GCM" in m_cipher: 98 m_cipher = re.sub(r"GCM-SHA\d\d\d", "GCM", m_cipher) 99 m_cipher = m_cipher+":+AEAD" 100 101 # Replace the last "-" with ":+" 102 else: 103 index = m_cipher.rindex("-") 104 m_cipher = m_cipher[:index] + ":+" + m_cipher[index+1:] 105 106 return m_cipher 107 108def translate_ossl(m_cipher): 109 """ 110 Translate m_cipher from Mbed TLS ciphersuite naming convention 111 and return the OpenSSL naming convention 112 """ 113 114 m_cipher = re.sub(r'^TLS-', '', m_cipher) 115 m_cipher = m_cipher.replace("-WITH", "") 116 117 # Remove the "-" from "ABC-xyz" 118 m_cipher = m_cipher.replace("AES-", "AES") 119 m_cipher = m_cipher.replace("CAMELLIA-", "CAMELLIA") 120 m_cipher = m_cipher.replace("ARIA-", "ARIA") 121 122 # Remove "RSA" if it is at the beginning 123 m_cipher = re.sub(r'^RSA-', r'', m_cipher) 124 125 # For all circumstances outside of PSK 126 if "PSK" not in m_cipher: 127 m_cipher = m_cipher.replace("-EDE", "") 128 m_cipher = m_cipher.replace("3DES-CBC", "DES-CBC3") 129 130 # Remove "CBC" if it is not prefixed by DES 131 m_cipher = re.sub(r'(?<!DES-)CBC-', r'', m_cipher) 132 133 # ECDHE-RSA-ARIA does not exist in OpenSSL 134 m_cipher = m_cipher.replace("ECDHE-RSA-ARIA", "ECDHE-ARIA") 135 136 # POLY1305 should not be followed by anything 137 if "POLY1305" in m_cipher: 138 index = m_cipher.rindex("POLY1305") 139 m_cipher = m_cipher[:index+8] 140 141 # If DES is being used, Replace DHE with EDH 142 if "DES" in m_cipher and "DHE" in m_cipher and "ECDHE" not in m_cipher: 143 m_cipher = m_cipher.replace("DHE", "EDH") 144 145 return m_cipher 146 147def format_ciphersuite_names(mode, names): 148 t = {"g": translate_gnutls, "o": translate_ossl}[mode] 149 return " ".join(t(c) for c in names) 150 151def main(target, names): 152 print(format_ciphersuite_names(target, names)) 153 154if __name__ == "__main__": 155 PARSER = argparse.ArgumentParser() 156 PARSER.add_argument('target', metavar='TARGET', choices=['o', 'g']) 157 PARSER.add_argument('names', metavar='NAMES', nargs='+') 158 ARGS = PARSER.parse_args() 159 main(ARGS.target, ARGS.names) 160