diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml
new file mode 100644
index 0000000..6a13cc1
--- /dev/null
+++ b/.github/workflows/python-app.yml
@@ -0,0 +1,42 @@
+# This workflow will install Python dependencies, run tests and lint with a single version of Python
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
+
+name: Python application
+
+on:
+ push:
+ tags:
+ - 'v*'
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+
+ runs-on: ubuntu-22.04
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install flake8 pytest
+ if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+ - name: Lint with flake8
+ run: |
+ # stop the build if there are Python syntax errors or undefined names
+ flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
+ # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
+ flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+ python3 schema_creator.py
+ python3 database_filler.py
+ - name: release
+ uses: ncipollo/release-action@v1
+ with:
+ artifacts: "requirements.db"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..26cfd53
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*/__pycache__/
+output.prisma
+requirements.db
+.env/
\ No newline at end of file
diff --git a/CREDITS.md b/CREDITS.md
new file mode 100644
index 0000000..691e16f
--- /dev/null
+++ b/CREDITS.md
@@ -0,0 +1,25 @@
+This file lists the main contributions. For the full list of contributions, please refer to the commit log.
+
+* Alessandro Tomasi ([@wry-run](https://www.github.com/wry-run))
+ - initial data extraction and structuring from
+ - ANSSI - Recommandations de sécurité relatives à TLS `v1.2`
+ - BSI - `TR-02102-2` and `TR-03116-4`
+ - NIST - `SP 800-52 Rev. 2`
+
+* Salvatore Manfredi ([@NetBender](https://www.github.com/NetBender))
+ - initial data review, formatting and translation
+ - data extraction from
+ - AgID - `AgID-RACCSECTLS-01`
+ - Mozilla - Recommended configurations `v5.6`
+ - NIST - `SP 800-131A Rev. 2`
+
+* Riccardo Germenia ([@Odinmylord](https://www.github.com/Odinmylord))
+ - data review, format standardization
+ - addition of contextual notes to store references and reasonings
+ - data extraction from
+ - Mozilla - Recommended configurations `v5.7`
+ - NIST - `SP 800-131A Rev. 2` and `SP 800-186`
+ - dataset structuring
+ - database schema definition (Prisma)
+ - reproducible scheme generation
+ - reproducible SQLite database generation
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..da6ab6c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,396 @@
+Attribution 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+ Considerations for licensors: Our public licenses are
+ intended for use by those authorized to give the public
+ permission to use material in ways otherwise restricted by
+ copyright and certain other rights. Our licenses are
+ irrevocable. Licensors should read and understand the terms
+ and conditions of the license they choose before applying it.
+ Licensors should also secure all rights necessary before
+ applying our licenses so that the public can reuse the
+ material as expected. Licensors should clearly mark any
+ material not subject to the license. This includes other CC-
+ licensed material, or material used under an exception or
+ limitation to copyright. More considerations for licensors:
+ wiki.creativecommons.org/Considerations_for_licensors
+
+ Considerations for the public: By using one of our public
+ licenses, a licensor grants the public permission to use the
+ licensed material under specified terms and conditions. If
+ the licensor's permission is not necessary for any reason--for
+ example, because of any applicable exception or limitation to
+ copyright--then that use is not regulated by the license. Our
+ licenses grant only permissions under copyright and certain
+ other rights that a licensor has authority to grant. Use of
+ the licensed material may still be restricted for other
+ reasons, including because others have copyright or other
+ rights in the material. A licensor may make special requests,
+ such as asking that all changes be marked or described.
+ Although not required by our licenses, you are encouraged to
+ respect those requests where reasonable. More considerations
+ for the public:
+ wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution 4.0 International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution 4.0 International Public License ("Public License"). To the
+extent this Public License may be interpreted as a contract, You are
+granted the Licensed Rights in consideration of Your acceptance of
+these terms and conditions, and the Licensor grants You such rights in
+consideration of benefits the Licensor receives from making the
+Licensed Material available under these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+ a. Adapted Material means material subject to Copyright and Similar
+ Rights that is derived from or based upon the Licensed Material
+ and in which the Licensed Material is translated, altered,
+ arranged, transformed, or otherwise modified in a manner requiring
+ permission under the Copyright and Similar Rights held by the
+ Licensor. For purposes of this Public License, where the Licensed
+ Material is a musical work, performance, or sound recording,
+ Adapted Material is always produced where the Licensed Material is
+ synched in timed relation with a moving image.
+
+ b. Adapter's License means the license You apply to Your Copyright
+ and Similar Rights in Your contributions to Adapted Material in
+ accordance with the terms and conditions of this Public License.
+
+ c. Copyright and Similar Rights means copyright and/or similar rights
+ closely related to copyright including, without limitation,
+ performance, broadcast, sound recording, and Sui Generis Database
+ Rights, without regard to how the rights are labeled or
+ categorized. For purposes of this Public License, the rights
+ specified in Section 2(b)(1)-(2) are not Copyright and Similar
+ Rights.
+
+ d. Effective Technological Measures means those measures that, in the
+ absence of proper authority, may not be circumvented under laws
+ fulfilling obligations under Article 11 of the WIPO Copyright
+ Treaty adopted on December 20, 1996, and/or similar international
+ agreements.
+
+ e. Exceptions and Limitations means fair use, fair dealing, and/or
+ any other exception or limitation to Copyright and Similar Rights
+ that applies to Your use of the Licensed Material.
+
+ f. Licensed Material means the artistic or literary work, database,
+ or other material to which the Licensor applied this Public
+ License.
+
+ g. Licensed Rights means the rights granted to You subject to the
+ terms and conditions of this Public License, which are limited to
+ all Copyright and Similar Rights that apply to Your use of the
+ Licensed Material and that the Licensor has authority to license.
+
+ h. Licensor means the individual(s) or entity(ies) granting rights
+ under this Public License.
+
+ i. Share means to provide material to the public by any means or
+ process that requires permission under the Licensed Rights, such
+ as reproduction, public display, public performance, distribution,
+ dissemination, communication, or importation, and to make material
+ available to the public including in ways that members of the
+ public may access the material from a place and at a time
+ individually chosen by them.
+
+ j. Sui Generis Database Rights means rights other than copyright
+ resulting from Directive 96/9/EC of the European Parliament and of
+ the Council of 11 March 1996 on the legal protection of databases,
+ as amended and/or succeeded, as well as other essentially
+ equivalent rights anywhere in the world.
+
+ k. You means the individual or entity exercising the Licensed Rights
+ under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+ a. License grant.
+
+ 1. Subject to the terms and conditions of this Public License,
+ the Licensor hereby grants You a worldwide, royalty-free,
+ non-sublicensable, non-exclusive, irrevocable license to
+ exercise the Licensed Rights in the Licensed Material to:
+
+ a. reproduce and Share the Licensed Material, in whole or
+ in part; and
+
+ b. produce, reproduce, and Share Adapted Material.
+
+ 2. Exceptions and Limitations. For the avoidance of doubt, where
+ Exceptions and Limitations apply to Your use, this Public
+ License does not apply, and You do not need to comply with
+ its terms and conditions.
+
+ 3. Term. The term of this Public License is specified in Section
+ 6(a).
+
+ 4. Media and formats; technical modifications allowed. The
+ Licensor authorizes You to exercise the Licensed Rights in
+ all media and formats whether now known or hereafter created,
+ and to make technical modifications necessary to do so. The
+ Licensor waives and/or agrees not to assert any right or
+ authority to forbid You from making technical modifications
+ necessary to exercise the Licensed Rights, including
+ technical modifications necessary to circumvent Effective
+ Technological Measures. For purposes of this Public License,
+ simply making modifications authorized by this Section 2(a)
+ (4) never produces Adapted Material.
+
+ 5. Downstream recipients.
+
+ a. Offer from the Licensor -- Licensed Material. Every
+ recipient of the Licensed Material automatically
+ receives an offer from the Licensor to exercise the
+ Licensed Rights under the terms and conditions of this
+ Public License.
+
+ b. No downstream restrictions. You may not offer or impose
+ any additional or different terms or conditions on, or
+ apply any Effective Technological Measures to, the
+ Licensed Material if doing so restricts exercise of the
+ Licensed Rights by any recipient of the Licensed
+ Material.
+
+ 6. No endorsement. Nothing in this Public License constitutes or
+ may be construed as permission to assert or imply that You
+ are, or that Your use of the Licensed Material is, connected
+ with, or sponsored, endorsed, or granted official status by,
+ the Licensor or others designated to receive attribution as
+ provided in Section 3(a)(1)(A)(i).
+
+ b. Other rights.
+
+ 1. Moral rights, such as the right of integrity, are not
+ licensed under this Public License, nor are publicity,
+ privacy, and/or other similar personality rights; however, to
+ the extent possible, the Licensor waives and/or agrees not to
+ assert any such rights held by the Licensor to the limited
+ extent necessary to allow You to exercise the Licensed
+ Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this
+ Public License.
+
+ 3. To the extent possible, the Licensor waives any right to
+ collect royalties from You for the exercise of the Licensed
+ Rights, whether directly or through a collecting society
+ under any voluntary or waivable statutory or compulsory
+ licensing scheme. In all other cases the Licensor expressly
+ reserves any right to collect such royalties.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+ a. Attribution.
+
+ 1. If You Share the Licensed Material (including in modified
+ form), You must:
+
+ a. retain the following if it is supplied by the Licensor
+ with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed
+ Material and any others designated to receive
+ attribution, in any reasonable manner requested by
+ the Licensor (including by pseudonym if
+ designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of
+ warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the
+ extent reasonably practicable;
+
+ b. indicate if You modified the Licensed Material and
+ retain an indication of any previous modifications; and
+
+ c. indicate the Licensed Material is licensed under this
+ Public License, and include the text of, or the URI or
+ hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any
+ reasonable manner based on the medium, means, and context in
+ which You Share the Licensed Material. For example, it may be
+ reasonable to satisfy the conditions by providing a URI or
+ hyperlink to a resource that includes the required
+ information.
+
+ 3. If requested by the Licensor, You must remove any of the
+ information required by Section 3(a)(1)(A) to the extent
+ reasonably practicable.
+
+ 4. If You Share Adapted Material You produce, the Adapter's
+ License You apply must not prevent recipients of the Adapted
+ Material from complying with this Public License.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+ to extract, reuse, reproduce, and Share all or a substantial
+ portion of the contents of the database;
+
+ b. if You include all or a substantial portion of the database
+ contents in a database in which You have Sui Generis Database
+ Rights, then the database in which You have Sui Generis Database
+ Rights (but not its individual contents) is Adapted Material; and
+
+ c. You must comply with the conditions in Section 3(a) if You Share
+ all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+ a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+ EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+ AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+ IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+ WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+ ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+ KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+ ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+ b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+ TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+ NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+ INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+ COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+ USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+ DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+ IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+ c. The disclaimer of warranties and limitation of liability provided
+ above shall be interpreted in a manner that, to the extent
+ possible, most closely approximates an absolute disclaimer and
+ waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+ a. This Public License applies for the term of the Copyright and
+ Similar Rights licensed here. However, if You fail to comply with
+ this Public License, then Your rights under this Public License
+ terminate automatically.
+
+ b. Where Your right to use the Licensed Material has terminated under
+ Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided
+ it is cured within 30 days of Your discovery of the
+ violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ For the avoidance of doubt, this Section 6(b) does not affect any
+ right the Licensor may have to seek remedies for Your violations
+ of this Public License.
+
+ c. For the avoidance of doubt, the Licensor may also offer the
+ Licensed Material under separate terms or conditions or stop
+ distributing the Licensed Material at any time; however, doing so
+ will not terminate this Public License.
+
+ d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+ License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+ a. The Licensor shall not be bound by any additional or different
+ terms or conditions communicated by You unless expressly agreed.
+
+ b. Any arrangements, understandings, or agreements regarding the
+ Licensed Material not stated herein are separate from and
+ independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+ a. For the avoidance of doubt, this Public License does not, and
+ shall not be interpreted to, reduce, limit, restrict, or impose
+ conditions on any use of the Licensed Material that could lawfully
+ be made without permission under this Public License.
+
+ b. To the extent possible, if any provision of this Public License is
+ deemed unenforceable, it shall be automatically reformed to the
+ minimum extent necessary to make it enforceable. If the provision
+ cannot be reformed, it shall be severed from this Public License
+ without affecting the enforceability of the remaining terms and
+ conditions.
+
+ c. No term or condition of this Public License will be waived and no
+ failure to comply consented to unless expressly agreed to by the
+ Licensor.
+
+ d. Nothing in this Public License constitutes or may be interpreted
+ as a limitation upon, or waiver of, any privileges and immunities
+ that apply to the Licensor or You, including from the legal
+ processes of any jurisdiction or authority.
+
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ee93a34
--- /dev/null
+++ b/README.md
@@ -0,0 +1,69 @@
+# TLS Compliance Dataset
+[![License: CC BY 4.0](https://img.shields.io/badge/License-CC_BY_4.0-lightgrey.svg)](https://creativecommons.org/licenses/by/4.0/)
+
+The content of this repository is the result of the gathering, translation, standardization and structuring of a set of technical requirements extracted from five cybersecurity agencies' guidelines.
+
+The examined guidelines are:
+- **AgID** [AgID-RACCSECTLS-01](https://cert-agid.gov.it/wp-content/uploads/2020/11/AgID-RACCSECTLS-01.pdf)
+- **ANSSI** [v1.2](https://cyber.gouv.fr/sites/default/files/2017/07/anssi-guide-recommandations_de_securite_relatives_a_tls-v1.2.pdf)
+- **BSI** [TR-02102-2](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TG02102/BSI-TR-02102-2.html) and [TR-03116-4](https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR03116/BSI-TR-03116-4.html)
+- **Mozilla** [v5.7](https://wiki.mozilla.org/Security/Server_Side_TLS)
+- **NIST** [SP 800-52 Rev. 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf) (and related documents)
+
+
+The result of this process is a dataset that can be audited, inspected and peer reviewed.
+
+#### Glossary
+- Configurable element - each element whose availability can be set by the system administrator (e.g., specific protocol versions);
+- Guideline - a document issued by a national cybersecurity agency that contains technical requirement for various configurable elements;
+- Level - the requirement level that every guideline assigns to each configurable element;
+- Profile - a use case defined by a guideline (e.g., customer-facing and government-facing service);
+- Variant - subset of elements that can have alternative configurations levels (e.g., prioritization of ephemeral keys use);
+- Condition - an additional requirement that restricts the use of a configurable element (e.g., "use *element* up to year 2030").
+
+## Dataset Structure
+The dataset is composed of multiple `.md` files that are used to store different sets of configurable elements. Each file contains a table with the following structure:
+| Configurable Element | (Guideline1, Profile1) | (Guideline1, Profile1) | ... | (Guidelinen, Profilen) | (Guidelinen, Profilen) |
+| -------------------- | --------------------- | ----------------------- | --- | --------------------- | ----------------------- |
+| Element1 | Level1,1 | Condition1,1 | ... | Level1,n | Condition1,n |
+| Element2 | Level2,1 | Condition2,1 | ... | Level2,n | Condition2,n |
+| ... | ... | ... | ... | ... | ... |
+| Elementn | Leveln,1 | Conditionn,1 | ... | Leveln,n | Conditionn,n |
+
+
+#### Additional Information
+For additional information on how to add a new guideline or a new table to the dataset, please refer to the [Standard Compliance Module]().
+
+### Configurable Elements
+
+Each file lists the requirements level of
+
+- Certificates-related
+ - [Certificate.md](): common fields;
+ - [Certificate Extensions.md](): specific extensions;
+ - [Certificate Signature.md](): algorithms that can be used to sign a certificate;
+- [Cipher Suites.md](): cipher suites;
+- [Hash Algorithms.md](): the hash algorithms that can be used together with the respective [signatures]();
+- [Key Lengths.md](): the key lengths that can be used for both key exchange and signature algorithms;
+- [Misc.md](): other configurable elements that do not fall into a specific category (e.g., vulnerability-specific mitigations);
+- [Protocol.md](): SSL and TLS versions;
+- [Signature Algorithms.md](): the signature algorithms that can be used for forward secrecy;
+- [TLS Extensions.md](): the TLS extensions that can be used during a secure transmission.
+
+## Reproducibility
+To enable reproducibility of the dataset, the repository also contains a set of scripts that can be used to generate a SQLite database that maps the dataset.
+
+- `schema_creator.py`: reads the dataset and by using [Prisma Client Python](https://prisma-client-py.readthedocs.io/en/stable/) creates an empty SQLite database with the tables needed to store the dataset. The database will be stored in a file called `requirements.db` (in the root directory of the repository);
+- `database_filler.py`: reads the dataset and fills all the tables of `requirements.db` with the data contained in the dataset.
+
+## How to contribute
+Please refer to the [Wiki](https://github.com/stfbk/tls-compliance-dataset/wiki) page.
+
+
+## Related Projects
+
+This dataset is the result of a study aimed to design a methodology to assess the compliance level of new and existing webservers. More detail about the methodology and the process behind its creation can be found in the paper *Automating Compliance for Improving TLS Security Postures: An Assessment of Public Administration Endpoints*.
+
+
+
+The content of this dataset is an integral part of [TLSAssistant](https://github.com/stfbk/tlsassistant), an open-source modular framework capable of identifying a wide range of TLS vulnerabilities and assessing compliance with multiple guidelines. Its actionable report can assist the user in correctly and easily fixing their configurations.
diff --git a/database_filler.py b/database_filler.py
new file mode 100755
index 0000000..6efbdff
--- /dev/null
+++ b/database_filler.py
@@ -0,0 +1,316 @@
+import sqlite3
+from copy import deepcopy
+from typing import Tuple
+
+import pandas as pd
+
+from utils.configs import sheets_mapping, different_names_pos, sheet_columns, guidelines, converters
+from utils.filler_utils import get_requirements_columns, get_columns_count_for_guideline, split_sheet, \
+ get_version_name_for_database, get_guideline_name_for_database, is_double_guideline, get_first_col_for_guideline, \
+ get_column, read_dataframes
+
+dataframe = read_dataframes({"converters":converters, "dtype":str})
+for df in dataframe:
+ dataframe[df] = dataframe[df].applymap(lambda x: x.strip() if isinstance(x, str) else x)
+
+sheet_with_extra_table = {
+ "TLS extensions": ("applies to version", "TlsVersionExtension")
+}
+
+conn = sqlite3.connect("requirements.db")
+cur = conn.cursor()
+
+
+def prepare_database():
+ cur.execute("SELECT name FROM sqlite_master WHERE type='table'")
+ for table in cur.fetchall():
+ cur.execute("DELETE FROM " + table[0])
+ conn.commit()
+
+
+def insert_guideline_info():
+ cur.executemany("INSERT OR REPLACE INTO Guideline VALUES (?, ?)",
+ [(guideline, guidelines[guideline]) for guideline in guidelines])
+
+
+def get_cell_for_df(df: pd.DataFrame, row_index: int, header):
+ col_index = 0
+ for col_index, col in enumerate(df.columns):
+ if col[0] == header[0]:
+ break
+ return df.iloc[row_index: row_index + 1, col_index:col_index + 1].iat[0, 0]
+
+
+def get_name_from_index_for_sheet(index, sheet_name: str) -> str:
+ """
+ Gets the name of the item for that row. Some sheets have the name column in a different position, for that case
+ see the different_names_pos dictionary
+ :param index: row index
+ :param sheet_name: sheet in which the search should be done
+ :return: item_name: the name for the row at index in the sheet
+ """
+ column = different_names_pos.get(sheet_name, (0, 1))[0]
+ return dataframe[sheet_name].iloc[index:index + 1, column:column + 1].iat[0, 0]
+
+
+def get_additional_info(index, sheet_name: str):
+ column, lengths = different_names_pos.get(sheet_name, (0, 1))
+ return_vals = []
+ tmp_df = dataframe[sheet_name].iloc[index:index + 1, column:column + lengths]
+ if lengths > 1:
+ for i in range(1, lengths):
+ val = tmp_df.iat[0, i]
+ return_vals.append(val)
+ return return_vals
+
+
+def already_parsed(col_name: str) -> bool:
+ for _, c2 in sheet_with_extra_table.items():
+ if c2[0] == col_name.strip():
+ return True
+ return False
+
+
+def values_to_add(r: pd.Series, columns: pd.Index) -> Tuple:
+ """Given a series of values checks if those values belong to columns that were already parsed
+ :param r The row (Series) containing the values that need to be checked
+ :param columns: The columns of the dataframe from which the row is taken
+ """
+ val_list = r.to_list()
+ i = 0
+ for c in columns:
+ if already_parsed(c[0]):
+ val_list.pop(i)
+ else:
+ i += 1
+ return tuple(val_list)
+
+
+def has_extra_table(sheet_name: str) -> Tuple:
+ return sheet_with_extra_table.get(sheet_name, ())
+
+
+def fill_extra_table(sheet_name: str) -> bool:
+ """
+ This function takes the name of a sheet as a param, uses it to get the column names from which it should get data
+ and the table in which to insert the data using the sheet_with_extra_table dictionary and then adds this data to the
+ database.
+
+ :param sheet_name: the sheet that has an extra table
+ :return: False if the sheet doesn't have an extra table, True if it committed to the database
+ """
+ column, table = sheet_with_extra_table.get(sheet_name, (None, None))
+ if not column or not table:
+ return False
+ file_sheet: pd.DataFrame = dataframe[sheet_name]
+ # The first column is almost always the names column
+ names: pd.Series = get_column(file_sheet, 0)
+ # Get only the columns that must be inserted in the extra table
+ versions = file_sheet.filter(like=column)
+ versions_names = {}
+ insertion_query = f"INSERT OR REPLACE INTO {table} VALUES (?, ?)"
+ values_to_insert = []
+ # prepare the mapping from index to column
+ for pos, version in enumerate(versions.columns.to_list()):
+ versions_names[pos] = version[1]
+
+ for pos, content in versions.iterrows():
+ name = names[pos]
+ # This variable i is used to cycle through the column's name without having to add it to the dataframe
+ # It can probably be avoided by using the join in pandas, but I can't get it to work
+ i = 0
+ for c in content:
+ if pd.notna(c):
+ values_to_insert.append(
+ (versions_names[i % len(versions.columns)], name))
+ i += 1
+ cur.executemany(insertion_query, values_to_insert)
+ conn.commit()
+ return True
+
+
+if __name__ == "__main__":
+ prepare_database()
+ insert_guideline_info()
+ guidelines_mapping = {}
+ for guideline in guidelines:
+ guidelines_mapping[guideline.upper()] = guideline
+ for sheet in dataframe:
+ sheet_mapped = sheets_mapping.get(sheet.strip())
+ if isinstance(sheet, str) and sheet_mapped:
+ done = False
+ values = []
+ if has_extra_table(sheet):
+ fill_extra_table(sheet)
+ general_dataframe, guidelines_dataframe = split_sheet(dataframe[sheet])
+ values_tuple = ()
+ # old_values is needed for some strange cases like key_signature
+ old_values = []
+ for row in general_dataframe.iterrows():
+ # row[0] is the index, row[1] is the actual content of the line
+ values_tuple = values_to_add(row[1], general_dataframe.columns)
+ if not len(old_values):
+ old_values = [v for v in values_tuple]
+ else:
+ tmp_list = []
+ for i, v in enumerate(values_tuple):
+ if pd.isna(v) and v != old_values[i]:
+ tmp_list.append(old_values[i])
+ else:
+ tmp_list.append(v)
+ values_tuple = tuple(tmp_list)
+ old_values = tmp_list
+ if values_tuple[0] != "Certificate Type":
+ values.append(values_tuple)
+ values_string = "("
+ values_string += "?," * len(values_tuple)
+ # Remove last ',' and replace it with ')'
+ values_string = values_string[:-1] + ")"
+ sql_query = f"INSERT OR REPLACE INTO {sheet_mapped} VALUES " + values_string
+ cur.executemany(sql_query, values)
+ conn.commit()
+ values = []
+
+ # Start of guideline specific part
+ requirements_columns = get_requirements_columns(guidelines_dataframe, sheet)
+ guidelines_columns_count = get_columns_count_for_guideline(guidelines_dataframe)
+
+ values_dict = {}
+ last_item = ""
+
+ # maybe this whole part can be rewritten using iloc
+ old_name = ""
+ for row in guidelines_dataframe.iterrows():
+ row_dictionary = row[1].to_dict()
+ for header in row_dictionary:
+ # header[0] is guideline_name
+ item_name = get_name_from_index_for_sheet(row[0], sheet)
+ old_name = item_name
+ guideline = get_guideline_name_for_database(header[0])
+ version_name = get_version_name_for_database(header[1])
+ table_name = sheet_mapped + guideline + version_name
+ content = row_dictionary[header]
+ if header[1] in requirements_columns[header[0]]:
+ # This is the case for sheets like cipher suite
+ if sheet_columns.get(sheet, {}).get(header[0]):
+ level_column = get_first_col_for_guideline(guidelines_dataframe, guideline)
+ level = get_cell_for_df(guidelines_dataframe, row[0], (guideline, level_column))
+ # If the cell is empty and the level isn’t negative (must not, not recommended)
+ # then "must not" is used as the level.
+ if level == "":
+ content = level
+ if pd.notna(content) or level in ["not recommended", "must not"]:
+ if content not in ["recommended", "must"]:
+ content = level
+ else:
+ content = "must not"
+
+ # this block is to prepare the dictionary
+ if not values_dict.get(table_name):
+ values_dict[table_name] = {row[0]: []}
+ if not values_dict[table_name].get(row[0]):
+ values_dict[table_name][row[0]] = []
+ # end of the block
+
+ # Vertically merged cells contain the value only in the first cell
+ if pd.isna(item_name) and not pd.isna(content):
+ item_name = values_dict[table_name][row[0] - 1][0]
+
+ # First the guideline name is added
+ values_dict[table_name][row[0]].append(guidelines_mapping.get(guideline, guideline))
+
+ # Then the name of the row is added
+ values_dict[table_name][row[0]].append(item_name)
+ # If this table needs extra data it gets added here
+ for el in get_additional_info(row[0], sheet):
+ values_dict[table_name][row[0]].append(el)
+
+ values_dict[table_name][row[0]].append(content)
+
+ elif pd.notna(header[1]) and \
+ get_first_col_for_guideline(guidelines_dataframe, header[0]) != header[1]:
+ # update all the lists of the same guideline with the condition
+ columns_to_apply = []
+ if " [" in header[1]:
+ columns_to_apply = header[1].split(" [")[1].replace("]", "").split(",")
+ columns_to_apply = [int(c.strip()) for c in columns_to_apply]
+ counter = 0
+ for t_name in values_dict:
+ guideline_db_name = get_guideline_name_for_database(header[0])
+ # this is needed only for the case of KeyLengthsBSI and KeyLengths BSI (from ...)
+ has_valid_underscore = "_" in guideline_db_name and "_" in t_name
+ if t_name.startswith(sheet_mapped + guideline_db_name):
+ if "_" not in t_name or has_valid_underscore:
+ counter += 1
+ if " [" in header[1] and counter not in columns_to_apply:
+ continue
+ values_dict[t_name][row[0]].append(content)
+ if is_double_guideline(header[0]):
+ tokens = header[0].split("+")
+ base_guideline = tokens[0].replace("(", "").strip()
+ for other_guideline in tokens[1:]:
+ other_name = get_guideline_name_for_database(other_guideline)
+ other_table = sheet_mapped + other_name + version_name
+ values_dict[other_table] = deepcopy(values_dict[table_name])
+ for el in values_dict[other_table]:
+ # Update the guideline name
+ for i, entry in enumerate(values_dict[other_table][el]):
+ if isinstance(entry, str) and entry.upper() == base_guideline.upper():
+ values_dict[other_table][el][i] = other_name
+
+ # Convert all the data into tuples to add them to the database and group them by guideline name
+ values_groups = {}
+ for table in values_dict:
+ # Get the number of columns for the actual table
+ table_columns_count = len(cur.execute(f"PRAGMA table_info({table})").fetchall())
+ entries = values_dict[table]
+
+ # # This is to prevent the "this or X" condition to appear in tables that don't need it
+ # # this condition checks if the guideline has multiple versions for this sheet
+ # if table.startswith("Protocol") and table[len("Protocol"):] not in [g.upper() for g in guidelines]:
+ # for entry in entries:
+ # entry = entries[entry]
+ # # Since the problem is a condition, and it only verifies if there are four elements.
+ # # Last element is the condition
+ # # Second to last is the level
+ # print(entry)
+ # if len(entry) > 3 and pd.notna(entry[-1]):
+ # if entry[-2][-1] != "°":
+ # entry[-1] = None
+ last_level = None
+
+ # This is to prevent the "this or X" condition to appear in tables that don't need it, only works
+ # for the case of Protocol sheet and only if the conditions are in adjacent lines
+ if table.startswith("Protocol"):
+ for index, entry in entries.items():
+ # skip first element
+ if index == 0:
+ continue
+ if len(entry) > 3 and pd.notna(entry[-1]) and pd.notna(entries[index - 1][-1]):
+ if entry[-2] != entries[index - 1][-2]:
+ entry[-1] = None
+ entries[index - 1][-1] = None
+
+ if not values_groups.get(table):
+ values_groups[table] = []
+ for index in entries:
+ entry = entries[index]
+ if pd.notna(entry[1]) and entry[1] != "Certificate Type" and entry[1] != "NIST":
+ # The double check is needed because of the case Mozilla + AGID which share the same pointer to
+ # the list of values
+ if len(entry) < table_columns_count:
+ entry.insert(0, index)
+ # Every remaining column is filled with None
+ while len(entry) < table_columns_count:
+ entry.append(None)
+ values_groups[table].append(tuple(entry))
+ for table in values_groups:
+ values = values_groups[table]
+ values_string = "("
+ # The values list should contain tuples that are all the same size
+ values_string += "?," * (len(values[0]))
+ # Remove last ',' and replace it with ')'
+ values_string = values_string[:-1] + ")"
+ sql_query = f"INSERT OR REPLACE INTO {table} VALUES " + values_string
+ cur.executemany(sql_query, values)
+ conn.commit()
diff --git a/markdown/Certificate Extensions.md b/markdown/Certificate Extensions.md
new file mode 100644
index 0000000..ce0ac16
--- /dev/null
+++ b/markdown/Certificate Extensions.md
@@ -0,0 +1,67 @@
+ | Extension | ('NIST', '') | ('NIST', 'critical') [^2] | ('NIST', 'condition') | ('BSI', '') [^1] | ('BSI', 'critical') | ('BSI', 'condition') | ('ANSSI', '') | ('ANSSI', 'condition') | ('MOZILLA', '') |
+ | :------------------------------- | :---------------- | :------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | :------------------ | :---------------------------------------------------------------------------------------------------------------------- | :-------------------- | :--------------------------------------------------------- | :---------------- |
+ | authorityKeyIdentifier | recommended | | CHECK_AKI [^3] | \ | | | must [^4] | | \ |
+ | subjectKeyIdentifier [^5] | recommended | | NOTE_ALWAYS The guidelines state `Same as in Public-Key Cryptography Standards [PKCS 10] request or calculated by the issuing CA`. The tool can not verify this condition since this specific check can only be performed by monitoring the endpoint during the certificate issuing phase [^6] | \ | | | | | \ |
+ | keyUsage [^7] | recommended | yes | (VALUE CertificateExtensions Digital Signature in keyUsage OR VALUE CertificateExtensions Key Agreement in keyUsage) and NOTE_FALSE Invalid keyUsage, allowed key usages are digital signature if using RSA certificate and key agreement if using ECDH or DH certificate [^8] | must | yes | | must [^9] | | \ |
+ | extendedKeyUsage [^10] | must [^11] | | NOTE_FALSE Issue detected: {reason} within certificate #{cert} AND VALUE CertificateExtensions TLS Web Server Authentication in extendedKeyUsage [^12] | recommended | | | must [^13] | | \ |
+ | extendedKeyUsage [^14] | must not | | (NOTE_TRUE Issue detected: {reason} within certificate #{cert} AND VALUE CertificateExtensions Any Extended Key Usage in extendedKeyUsage) OR CHECK_SAME_KEYUSAGE [^15] | \ | | | | | \ |
+ | Certificate Policies | optional | | | \ | | | \ | | \ |
+ | subjectAltName [^16] | must [^17] | | VALUE CertificateExtensions DNS in subjectAltName OR VALUE CertificateExtensions IP Address in subjectAltName [^18] | must not | | NOTE_TRUE Issue detected: {reason} within certificate #{cert} and VALUE CertificateExtensions * in subjectAltName [^19] | must [^20] | | \ |
+ | authorityInfoAccess [^21] | must [^22] | | VALUE CertificateExtensions OCSP - URI in authorityInfoAccess and NOTE_FALSE the authorityInfoAccess extension must have a field CA Issuers containing HTTP URL for certificates issued to issuing CA [^23] | must | | THIS or CertificateExtensions CRL Distribution Points | must | THIS or CertificateExtensions CRL Distribution Points | \ |
+ | authorityInfoAccess | must [^24] | | VALUE CertificateExtensions CA ISSUERS - URI in authorityInfoAccess and NOTE_FALSE the authorityInfoAccess extension must have the Online Certificate Status Protocol and it must contain HTTP URL for the issuing CA OCSP responder [^25] | | | | | | \ |
+ | crlDistributionPoints [^26] | optional | | VALUE CertificateExtensions IP in crlDistributionPoints OR VALUE CertificateExtensions URI in crlDistributionPoints [^27] | must | | THIS or CertificateExtensions Authority Information Access | must | THIS or CertificateExtensions Authority Information Access | \ |
+ | crlDistributionPoints | must not | | NOTE_TRUE Issue detected: {reason} within certificate #{cert} AND (VALUE CertificateExtensions Relative Name in crlDistributionPoints OR VALUE CertificateExtensions CRL Issuer in crlDistributionPoints OR VALUE CertificateExtensions Reasons in crlDistributionPoints) AND DISABLE_IF False [^28] | | | | | | \ |
+ | ct_precert_scts [^29] | optional [^30] | | | | | | \ | | \ |
+ | OCSP must staple extension [^31] | optional [^32] | | | | | | \ | | \ |
+
+[^1]: BSI TR-03116-4
+[^2]: Each extension in a certificate is designated as either critical or non-critical. A certificate-using system MUST reject the certificate if it encounters a critical extension it does not recognize or a critical extension that contains information that it cannot process. A non-critical extension MAY be ignored if it is not recognized, but MUST be processed if it is recognized. The following sections present recommended extensions used within Internet certificates and stand
+
+ https://www.rfc-editor.org/rfc/rfc5280 section 4.2
+[^3]: Same as subject key identifier in issuing CA certificate; Prohibited: Issuer DN, Serial Number tuple
+[^4]: R32
+[^5]: Subject Key Identifier (SKI)
+[^6]: Same as in Public-Key Cryptography Standards (PKCS) 10 request or calculated by the issuing CA
+[^7]: Key Usage
+[^8]: RSA, ECDSA, or DSA signature certificate: digital signature;
+ ECDH or DH certificate: key agreement
+[^9]: R27
+[^10]: Extended Key Usage
+[^11]: server
+[^12]: id-kp-serverAuth {1 3 6 1 5 5 7 3 1}
+ http://oid-info.com/get/1.3.6.1.5.5.7.3.1
+[^13]: R28
+[^14]: Extended Key Usage
+[^15]: the keyAgreement and keyEncipherments are considered mutually exclusive as show in section 5.4.3 of https://www.etsi.org/deliver/etsi_ts/102200_102299/102280/01.01.01_60/ts_102280v010101p.pdf
+[^16]: Subject Alternative Name (SAN)
+[^17]: Required. Multiple SANs are permitted, e.g., for load balanced environments.
+[^18]: DNS host name, or IP address if there is no DNS name assigned. Other name forms may be included, if appropriate.
+[^19]: use wildcards in CN
+[^20]: R29
+[^21]: Authority Information Access
+[^22]: Required. Multiple SANs are permitted, e.g., for load balanced environments.
+[^23]: field id-ad-caIssuers
+[^24]: Required. Multiple SANs are permitted, e.g., for load balanced environments.
+[^25]: field id-ad-caIssuers
+[^26]: CRL Distribution Points
+[^27]: HTTP value in distributionPoint
+ field pointing to a full and complete CRL
+
+ We don't check if the CRL is full and complete. We only check if the value in the cert is of type IP or URI
+[^28]: A CRL is indicated by a DistributionPoint ::= SEQUENCE.
+ This SEQUENCE can contain three items:
+ distributionPoint: DistributionPointName
+ reasons: ReasonFlags
+ cRLIssuer: GeneralNames.
+
+ The DistributionPointName type is a CHOICE with two options:
+ fullName
+ nameRelativeToCRLIssuer
+
+ A valid distributionPoint must not have the "reasons" and "cRLIssuer" fields and the distributionPoint can not be of type nameRelativeToCRLIssuer
+[^29]: Signed Certificate Timestamps List
+[^30]: Optional. This extension contains a sequence of Signed Certificate
+ Timestamps, which provide
+ evidence that the certificate has been submitted to Certificate Transparency logs.
+[^31]: TLS Certificate Status Request
+[^32]: Optional. This extension (sometimes referred to as the “must staple” extension) may be present to indicate to clients that the server supports OCSP stapling and will provide a stapled OCSP response when one is requested.
diff --git a/markdown/Certificate Signature.md b/markdown/Certificate Signature.md
new file mode 100644
index 0000000..00168ac
--- /dev/null
+++ b/markdown/Certificate Signature.md
@@ -0,0 +1,20 @@
+ | Certificate signature algorithm | IANA | TLS version | ('NIST', '') [^1] | ('NIST', 'Condition') | ('BSI', '') [^2] | ('BSI', 'Condition') [^5] | ('ANSSI', '') [^3] | ('MOZILLA (+AgID)', 'Modern') [^4] | ('MOZILLA (+AgID)', 'Intermediate') | ('MOZILLA (+AgID)', 'Old') |
+ | :----------------------------------- | ---: | ----------: | :------------------- | :--------------------------------------------------------------- | :------------------- | :------------------------------------------------------- | :------------------- | :--------------------------------- | :---------------------------------- | :------------------------- |
+ | anonymous | 0 | 1.2 | must not [^6] | | \ | | must not [^7] | \ | \ | \ |
+ | rsa [^8] | 1 | 1.2 | must | THIS or CertificateSignature ecdsa and CHECK_KEY_TYPE rsa [^9] | recommended | THIS or CertificateSignature dsa;ecdsa AND YEAR 2025 | optional | \ | \ | \ |
+ | dsa | 2 | 1.2 | \ | CHECK_KEY_TYPE dsa [^10] | recommended | THIS or CertificateSignature rsa;ecdsa AND YEAR 2029 | \ | \ | \ | \ |
+ | ecdsa | 3 | 1.2 | must | THIS or CertificateSignature rsa and CHECK_KEY_TYPE ecddsa [^11] | recommended | THIS or CertificateSignature rsa;dsa | recommended | \ | \ | \ |
+
+[^1]: SP800-52 section 3.2
+[^2]: BSI-TR-02102-2, 3.3.3 + 3.4.3
+[^3]: R8
+[^4]: Being a list of recommendations:
+
+ not mentioned --> not recommended
+[^5]: Not explicitally mentioned but required to match the mechanical check of the conditions with the NIST case
+[^6]: TLS servers conforming to this specification shall be configured with an RSA signature certificate or an ECDSA signature certificate
+[^7]: R8+R5
+[^8]: We consider RSASSA-PSS as a subset of RSA (as stated in ANSSI v1.2 R8)
+[^9]: At a minimum, TLS servers conforming to this specification shall be configured with an RSA signature certificate or an ECDSA signature certificate.
+[^10]: recommended "if key is DH"
+[^11]: At a minimum, TLS servers conforming to this specification shall be configured with an RSA signature certificate or an ECDSA signature certificate.
diff --git a/markdown/Certificate.md b/markdown/Certificate.md
new file mode 100644
index 0000000..c5c41d7
--- /dev/null
+++ b/markdown/Certificate.md
@@ -0,0 +1,6 @@
+| Certificate | ('NIST', '') | ('NIST', 'condition') | ('BSI', '') | ('BSI', 'condition') | ('ANSSI', '') | ('ANSSI', 'condition') | ('MOZILLA', '') |
+| :------------------------- | :----------- | :----------------------------------------------------------------------------------------------------------------------------- | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------- | ---------------------: | :---------------- |
+| X.509 version | must | VALUE Certificate 2 == X.509 version and NOTE_FALSE In order for the certificate to be compliant it has to use X.509 version 2 | \ | | must | | \ |
+| Issuer Distinguished Name | recommended | CHECK_DN Issuer Distinguished Name - der | must not | NOTE_TRUE BSI guidelines prohibit the usage of * in this field. Issue detected: {reason} within certificate #{cert} and VALUE Certificate * in [Issuer Distinguished Name][CN] | \ | | \ |
+| validity | recommended | YEARS <= 3 and NOTE_FALSE In order for the certificate to be compliant it should have a validity of 3 years or less | must | YEARS <= 3 and NOTE_FALSE In order for the certificate to be compliant it must have a validity of 3 years or less | \ | | \ |
+| Subject Distinguished Name | recommended | CHECK_DN Subject Distinguished Name - der | must not | NOTE_TRUE BSI guidelines prohibit the usage of * in this field. Issue detected: {reason} within certificate #{cert} and VALUE Certificate * in [Subject Distinguished Name][CN] | \ | | \ |
diff --git a/markdown/Cipher Suites.md b/markdown/Cipher Suites.md
new file mode 100644
index 0000000..62537d0
--- /dev/null
+++ b/markdown/Cipher Suites.md
@@ -0,0 +1,188 @@
+| MYSQL | IANA | ('NIST', 'Requirement') | ('NIST', 'Preferred #1') | ('NIST', 'Preferred #2') [^1] | ('NIST', 'Preferred #3') [^5] | ('NIST', 'Condition') [^6] | ('BSI', 'Requirement') [^7] | ('BSI', 'Preferred #1') | ('BSI', 'Preferred #2') [^2] | ('BSI', 'Federal req.') [^8] | ('BSI', 'Condition [3]') [^9] | ('ANSSI', '') [^10] | ('MOZILLA (+AgID)', 'Modern') [^11] | ('MOZILLA (+AgID)', 'Intermediate') [^3] | ('MOZILLA (+AgID)', 'Old') [^4] |
+| --------------------------------------------------- | :--------- | :---------------------- | :----------------------- | :----------------------------- | :---------------------------- | :-------------------------------------------- | :-------------------------- | :---------------------- | :--------------------------- | :---------------------------- | :---------------------------------------------------------- | :--------------------- | :---------------------------------- | :--------------------------------------- | :------------------------------ |
+| TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | 0xC0,0x2B | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | must | THIS or CIPHER TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | recommended | \ | recommended | recommended |
+| TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 | 0xC0,0x2C | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | ✓ | | recommended | \ | recommended | recommended |
+| TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 | 0xCC,0xA9 | must not | | | | | \ | | | | | recommended | \ | recommended | recommended |
+| TLS_ECDHE_ECDSA_WITH_AES_128_CCM | 0xC0,0xAC | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | ✓ | | recommended | \ | \ | \ |
+| TLS_ECDHE_ECDSA_WITH_AES_256_CCM | 0xC0,0xAD | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | ✓ | | recommended | \ | \ | \ |
+| TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 | 0xC0,0xAE | optional | ✓ | ✓ | | | \ | | | | | \ | \ | \ | \ |
+| TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 | 0xC0,0xAF | optional | ✓ | ✓ | | | \ | | | | | \ | \ | \ | \ |
+| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | 0xC0,0x23 | optional | ✓ | | | | recommended | ✓ | | must | THIS or CIPHER TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | not recommended | \ | \ | recommended |
+| TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 | 0xC0,0x24 | optional | ✓ | | | | recommended | ✓ | | ✓ | | not recommended | \ | \ | recommended |
+| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA | 0xC0,0x09 | optional | ✓ | | | | \ | | | | | \ | \ | \ | recommended |
+| TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA | 0xC0,0x0A | optional | ✓ | | | | \ | | | | | \ | \ | \ | recommended |
+| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | 0xC0,0x2F | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | must | THIS or CIPHER TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | recommended | \ | recommended | recommended |
+| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 | 0xC0,0x30 | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | ✓ | | recommended | \ | recommended | recommended |
+| TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 | 0xCC,0xA8 | must not | | | | | \ | | | | | recommended | \ | recommended | recommended |
+| TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 | 0xCC,0xAA | must not | | | | | \ | | | | | not recommended | \ | recommended | recommended |
+| TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 | 0x00,0x9E | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | ✓ | | not recommended | \ | recommended | recommended |
+| TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 | 0x00,0x9F | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | ✓ | | not recommended | \ | recommended | recommended |
+| TLS_DHE_RSA_WITH_AES_128_CCM | 0xC0,0x9E | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | ✓ | | not recommended | \ | \ | \ |
+| TLS_DHE_RSA_WITH_AES_256_CCM | 0xC0,0x9F | optional | ✓ | ✓ | | | recommended | ✓ | | ✓ | | \ | \ | \ | \ |
+| TLS_DHE_RSA_WITH_AES_128_CCM_8 | 0xC0,0xA2 | optional | ✓ | ✓ | | | \ | | | | | \ | \ | \ | \ |
+| TLS_DHE_RSA_WITH_AES_256_CCM_8 | 0xC0,0xA3 | optional | ✓ | ✓ | | | \ | | | | | \ | \ | \ | \ |
+| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | 0xC0,0x27 | optional | ✓ | | | | recommended | ✓ | | must | THIS or CIPHER TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | not recommended | \ | \ | recommended |
+| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 | 0xC0,0x28 | optional | ✓ | | | | recommended | ✓ | | ✓ | | not recommended | \ | \ | recommended |
+| TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 | 0x00,0x67 | optional | ✓ | | | | recommended | ✓ | | ✓ | | not recommended | \ | \ | recommended |
+| TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 | 0x00,0x6B | optional | ✓ | | | | recommended | ✓ | | ✓ | | not recommended | \ | \ | recommended |
+| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA | 0xC0,0x13 | optional | ✓ | | | | \ | | | | | \ | \ | \ | recommended |
+| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA | 0xC0,0x14 | optional | ✓ | | | | \ | | | | | \ | \ | \ | recommended |
+| TLS_DHE_RSA_WITH_AES_128_CBC_SHA | 0x00,0x33 | optional | ✓ | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_DHE_RSA_WITH_AES_256_CBC_SHA | 0x00,0x39 | optional | ✓ | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 | 0x00,0xA2 | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | ✓ | | \ | \ | \ | \ |
+| TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 | 0x00,0xA3 | optional | ✓ | ✓ | ✓ | | recommended | ✓ | | ✓ | | \ | \ | \ | \ |
+| TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 | 0x00,0x40 | optional | ✓ | | | | recommended | ✓ | | ✓ | | \ | \ | \ | \ |
+| TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 | 0x00,0x6A | optional | ✓ | | | | recommended | ✓ | | ✓ | | \ | \ | \ | \ |
+| TLS_DHE_DSS_WITH_AES_128_CBC_SHA | 0x00,0x32 | optional | ✓ | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_DHE_DSS_WITH_AES_256_CBC_SHA | 0x00,0x38 | optional | ✓ | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_DH_DSS_WITH_AES_128_GCM_SHA256 | 0x00,0xA4 | optional | | ✓ | ✓ | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_DH_DSS_WITH_AES_256_GCM_SHA384 | 0x00,0xA5 | optional | | ✓ | ✓ | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_DH_DSS_WITH_AES_128_CBC_SHA256 | 0x00,0x3E | optional | | | | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_DH_DSS_WITH_AES_256_CBC_SHA256 | 0x00,0x68 | optional | | | | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_DH_DSS_WITH_AES_128_CBC_SHA | 0x00,0x30 | optional | | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_DH_DSS_WITH_AES_256_CBC_SHA | 0x00,0x36 | optional | | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_DH_RSA_WITH_AES_128_GCM_SHA256 | 0x00,0xA0 | optional | | ✓ | ✓ | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_DH_RSA_WITH_AES_256_GCM_SHA384 | 0x00,0xA1 | optional | | ✓ | ✓ | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_DH_RSA_WITH_AES_128_CBC_SHA256 | 0x00,0x3F | optional | | | | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_DH_RSA_WITH_AES_256_CBC_SHA256 | 0x00,0x69 | optional | | | | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_DH_RSA_WITH_AES_128_CBC_SHA | 0x00,0x31 | optional | | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_DH_RSA_WITH_AES_256_CBC_SHA | 0x00,0x37 | optional | | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 | 0xC0,0x2D | optional | | ✓ | ✓ | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 | 0xC0,0x2E | optional | | ✓ | ✓ | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 | 0xC0,0x25 | optional | | | | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 | 0xC0,0x26 | optional | | | | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA | 0xC0,0x04 | optional | | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA | 0xC0,0x05 | optional | | | | | \ | | | | | \ | \ | \ | \ |
+| TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 | 0xC0,0x31 | optional | | ✓ | ✓ | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 | 0xC0,0x32 | optional | | ✓ | ✓ | | recommended | | ✓ | | | \ | \ | \ | \ |
+| TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 | 0xC0,0x29 | optional | | | | | recommended | | ✓ | | | \ | \ | \ | \