Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions sunbeam-python/sunbeam/features/interface/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ def generate_ca_chain(certificate: str, ca_certificate: str, ca_chain: str) -> s
if not certificate_decoded or not ca_certificate_decoded or not ca_chain_decoded:
raise binascii.Error("Unable to decode one of the certificates")

# Normalize line endings to LF to ensure consistent comparison and output.
# Certificates with CRLF line endings may otherwise be treated as different
# from equivalent certificates with LF line endings.
certificate_decoded = certificate_decoded.replace("\r\n", "\n")
ca_certificate_decoded = ca_certificate_decoded.replace("\r\n", "\n")
ca_chain_decoded = ca_chain_decoded.replace("\r\n", "\n")

# If ca_certificate is already part of ca_chain, do not add it to the final ca chain
# manual-tls-certificates checks if the final ca_chain is in proper order and each
# certificate is signed by the successor one.
Expand Down
26 changes: 26 additions & 0 deletions sunbeam-python/tests/unit/sunbeam/features/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,29 @@ def test_generate_ca_chain():
ca_chain_decoded = decode_base64_as_string(ca_chain)
expected_chain = cert1 + "\n" + cert2 + "\n" + cert3
assert ca_chain_decoded == expected_chain


def test_generate_ca_chain_deduplicates_ca_cert_with_crlf():
"""Test that ca_certificate with CRLF endings is not duplicated when

ca_chain already contains an equivalent certificate with LF endings.
"""
cert1 = "CERT1"
# ca_certificate uses CRLF line endings
cert2_crlf = (
"-----BEGIN CERTIFICATE-----\r\nISSUINGCA\r\n-----END CERTIFICATE-----\r\n"
)
# ca_chain already contains the same cert with LF line endings
cert2_lf = "-----BEGIN CERTIFICATE-----\nISSUINGCA\n-----END CERTIFICATE-----\n"
cert3 = "-----BEGIN CERTIFICATE-----\nROOTCA\n-----END CERTIFICATE-----\n"
ca_chain_input = cert2_lf + "\n" + cert3

ca_chain = generate_ca_chain(
encode_base64_as_string(cert1),
encode_base64_as_string(cert2_crlf),
encode_base64_as_string(ca_chain_input),
)
ca_chain_decoded = decode_base64_as_string(ca_chain)
# cert2 must appear only once in the output
assert ca_chain_decoded.count("ISSUINGCA") == 1
assert ca_chain_decoded.count("ROOTCA") == 1
Loading