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