-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for compress_certificate extension #491
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We keep class Certificate and class CompressedCertificate separate (instead of the latter being a wrapper around the first)
this will make it not transparent and complicate the rest of the code
Reviewed 1 of 1 files at r1, 1 of 1 files at r2, 1 of 1 files at r3, 1 of 1 files at r4, 1 of 1 files at r5, 2 of 2 files at r6, 1 of 1 files at r7, 5 of 5 files at r8, 1 of 1 files at r9, 2 of 2 files at r10, 2 of 2 files at r11, all commit messages.
Reviewable status: all files reviewed, 21 unresolved discussions (waiting on @charxhit)
-- commits
line 23 at r8:
fixup commit, so should be squashed
tlslite/constants.py
line 192 at r2 (raw file):
zstd = 3 all = (1, 2, 3) default = (1,)
we generally don't create such values and they will interact badly with enum handling code of extensions
Code quote:
all = (1, 2, 3)
default = (1,)
tlslite/extensions.py
line 2103 at r3 (raw file):
class CompressCertificateExtension(TLSExtension):
why it's not a VarListExtension
?
tlslite/extensions.py
line 2124 at r3 (raw file):
if not all([algo in CompressionAlgorithms.all for algo in self.advertised_algorithms]): raise TLSInternalError("Unknown algorithm provided for certificate compression")
for tlsfuzzer we generally want the ability to send unassigned values, making sure that the user can't set invalid values should happen like 2 API levels higher (in HandshakeSettings)
tlslite/extensions.py
line 2148 at r3 (raw file):
TLSExtension._universalExtensions = \
why CompressCertificateExtension
is not added here?
tlslite/extensions.py
line 2116 at r8 (raw file):
advertised_algorithms: an iterable of integers denoting which algorithms the peer supports for compression """
that's not correct Sphinx syntax for param docs
tlslite/extensions.py
line 2120 at r9 (raw file):
# generic code allows empty, this ext does not if not parser.getRemainingLength(): raise DecodeError("Empty payload in extension")
That's not what I see in the VarListExtension...
tlslite/extensions.py
line 2130 at r9 (raw file):
supports for compression """ return super(CompressCertificateExtension, self).create(advertised_algorithms)
why have it in the first place then?
tlslite/handshakesettings.py
line 384 at r4 (raw file):
# certificate compression extensions self.use_certificate_compression = False self.certificate_compression_algorithms = CompressionAlgorithms.default
why split use and list of allowed? why an empty list can't mean "don't use"?
tlslite/handshakesettings.py
line 616 at r4 (raw file):
if other.use_certificate_compression not in (True, False): raise ValueError("use_heartbeat_extension must be True or False")
it's no heartbeat
tlslite/handshakesettings.py
line 628 at r10 (raw file):
if other.use_certificate_compression not in (True, False): raise ValueError("use_certificate_compression must be True or False")
squash fixup commits, don't add them
tlslite/messages.py
line 24 at r5 (raw file):
from .extensions import * from .utils.format_output import none_as_unknown from utils import compression
use relative imports
tlslite/messages.py
line 24 at r7 (raw file):
from .extensions import * from .utils.format_output import none_as_unknown from .utils import compression
fixup commits need to be squashed as CI verifies that the tests pass after every commit
tlslite/messages.py
line 1375 at r8 (raw file):
Args: chosen_algorithm - An int denoting an algorithm to be used to create the compressed msg certificate_message - Object of class messages.Certificate whose data will be encoded
not valid Sphinx syntax
tlslite/tlsconnection.py
line 1281 at r6 (raw file):
certificate_request = None certificate = None compress_cert_ext = clientHello.getExtension(ExtensionType.compress_certificate)
no? The extension is negotiated when the server sends the extension, not when the client advertised support for it
tlslite/tlsconnection.py
line 1469 at r6 (raw file):
# Problem: it's unclear whether the rfc wants to us to send an alert incase certificate_verify message # contains a compress_certificate extension if we did not advertise one. For now, do nothing.
extensions in ClientHello and CertificateRequest are completely independent, so yes, server can send request for compression if we didn't advertise
tlslite/tlsconnection.py
line 1475 at r6 (raw file):
# Certificate message instead for requested_algo in verify_compress_cert_ext.advertised_algorithms: if requested_algo in hello_compress_cert_ext.advertised_algorithms:
client hello algorithms are irrelevant for this
tlslite/utils/compression.py
line 35 at r8 (raw file):
Raises: KeyError: if provided algorithm integer is unknown """
wrong Sphinx syntax
tlslite/utils/compression.py
line 48 at r8 (raw file):
BadCertificateError: if decompression failed ValueError: if chosen_algorithm is unknown """
wrong Sphinx syntax
tlslite/utils/compression.py
line 77 at r8 (raw file):
BadCertificateError: if compression failed ValueError: if chosen_algorithm is unknown """
wrong Sphinx syntax
unit_tests/test_tlslite_extensions.py
line 1962 at r11 (raw file):
class TestCompressCertificateExtension(unittest.TestCase):
code and test cases for it should be added in the same patch
5d520d3
to
2835eb4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarification: CompressedCertificate still depends on Certificate class to make sense of the compressed message, but unlike in the previous pull request, it doesn't attempt to act as a proxy to it. Can you give an example of a possible complication here so I understand what you are referring to?
Reviewable status: 3 of 9 files reviewed, 21 unresolved discussions (waiting on @tomato42)
Previously, tomato42 (Hubert Kario) wrote…
fixup commit, so should be squashed
Fixed in a now squashed commit
tlslite/constants.py
line 192 at r2 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
we generally don't create such values and they will interact badly with enum handling code of extensions
Removed from enum class and added to utils.compression
tlslite/extensions.py
line 2103 at r3 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
why it's not a
VarListExtension
?
Fixed in a now squashed commit
tlslite/extensions.py
line 2124 at r3 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
for tlsfuzzer we generally want the ability to send unassigned values, making sure that the user can't set invalid values should happen like 2 API levels higher (in HandshakeSettings)
Fixed in a now squashed commit
tlslite/extensions.py
line 2148 at r3 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
why
CompressCertificateExtension
is not added here?
Fixed in a now squashed commit
tlslite/extensions.py
line 2116 at r8 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
that's not correct Sphinx syntax for param docs
Fixed
tlslite/extensions.py
line 2120 at r9 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
That's not what I see in the VarListExtension...
Do you mean that VarListExtension rejects empty extension data fields as well when parsing? When I reviewed the parse() method it seemed to just set the internal_value
as None
in case of no extension payload...
tlslite/extensions.py
line 2130 at r9 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
why have it in the first place then?
So that IDE s and debugging tools can point to a relevant docstring which would make more sense than the super() function's one to someone going through the code. Do you prefer the code without this wrapper?
tlslite/handshakesettings.py
line 384 at r4 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
why split use and list of allowed? why an empty list can't mean "don't use"?
I wrote it like this to be more explicit and readable + provide a safe default for users that just want to enable certificate compression without knowing about the algorithms used. I can change the code to only accept one flag if you prefer it that way.
tlslite/handshakesettings.py
line 616 at r4 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
it's no heartbeat
Fixed in a now squashed commit
tlslite/handshakesettings.py
line 628 at r10 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
squash fixup commits, don't add them
Fixed in a now squashed commit
tlslite/messages.py
line 24 at r5 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
use relative imports
Fixed in a now squashed commit
tlslite/messages.py
line 24 at r7 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
fixup commits need to be squashed as CI verifies that the tests pass after every commit
Fixed in a now squashed commit
tlslite/messages.py
line 1375 at r8 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
not valid Sphinx syntax
Fixed
tlslite/tlsconnection.py
line 1281 at r6 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
no? The extension is negotiated when the server sends the extension, not when the client advertised support for it
That does not seem to be the case with certificate_extension. Relevant quotes from the rfc:
-
"Whenever [client_certificate] is sent by the client as a ClientHello message extension...it indicates support for compressed server certificates. Whenever it is sent by the server as a CertificateRequest extension...it indicates support for compressed client certificates"
-
"The compress_certificate extension is a unidirectional indication; no corresponding response extension is needed"
rfc suggests that unlike other extensions, formal negotiation for this extension is not required and the valid places for it include only the ClientHello and CertificateRequest
tlslite/tlsconnection.py
line 1469 at r6 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
extensions in ClientHello and CertificateRequest are completely independent, so yes, server can send request for compression if we didn't advertise
Thanks for the clarification, removed comment
tlslite/tlsconnection.py
line 1475 at r6 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
client hello algorithms are irrelevant for this
see above, it seems that they will remain relevant throughout the handshake because we will check the server's algorithm in CertificateRequest against the ones we advertised in ClientHello (again, a compress_certificate extension in ServerHello is not expected)
tlslite/utils/compression.py
line 35 at r8 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
wrong Sphinx syntax
Fixed
tlslite/utils/compression.py
line 48 at r8 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
wrong Sphinx syntax
Fixed
tlslite/utils/compression.py
line 77 at r8 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
wrong Sphinx syntax
Fixed
unit_tests/test_tlslite_extensions.py
line 1962 at r11 (raw file):
Previously, tomato42 (Hubert Kario) wrote…
code and test cases for it should be added in the same patch
Fixed
Commits should be fixed from now on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that other PR is related to this one: tlsfuzzer/tlsfuzzer#802
and just as a head's up: for it to be mergable both client and server side support needs to be implemented
(and sorry for the slow review, I was busy with urgent stuff at work)
Reviewed 6 of 6 files at r12, all commit messages.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on @charxhit)
-- commits
line 2 at r12:
the commit message should still describe what's being done
tlslite/extensions.py
line 2120 at r9 (raw file):
Previously, charxhit wrote…
Do you mean that VarListExtension rejects empty extension data fields as well when parsing? When I reviewed the parse() method it seemed to just set the
internal_value
asNone
in case of no extension payload...
aah, right, it's about the whole extension being empty, not extra data
tlslite/extensions.py
line 2130 at r9 (raw file):
Previously, charxhit wrote…
So that IDE s and debugging tools can point to a relevant docstring which would make more sense than the super() function's one to someone going through the code. Do you prefer the code without this wrapper?
I haven't been making them, and I didn't feel like they were missing, but despite the verbosity it's probably fine to have class specific doc text.
tlslite/handshakesettings.py
line 384 at r4 (raw file):
Previously, charxhit wrote…
I wrote it like this to be more explicit and readable + provide a safe default for users that just want to enable certificate compression without knowing about the algorithms used. I can change the code to only accept one flag if you prefer it that way.
but then, why disable it by default?
tlslite/tlsconnection.py
line 1281 at r6 (raw file):
Previously, charxhit wrote…
That does not seem to be the case with certificate_extension. Relevant quotes from the rfc:
"Whenever [client_certificate] is sent by the client as a ClientHello message extension...it indicates support for compressed server certificates. Whenever it is sent by the server as a CertificateRequest extension...it indicates support for compressed client certificates"
"The compress_certificate extension is a unidirectional indication; no corresponding response extension is needed"
rfc suggests that unlike other extensions, formal negotiation for this extension is not required and the valid places for it include only the ClientHello and CertificateRequest
aah, right, I was misremembering the RFC
tlslite/tlsconnection.py
line 1475 at r6 (raw file):
Previously, charxhit wrote…
see above, it seems that they will remain relevant throughout the handshake because we will check the server's algorithm in CertificateRequest against the ones we advertised in ClientHello (again, a compress_certificate extension in ServerHello is not expected)
based on what? Server doesn't have to limit extensions sent in CR to the ones advertised by client
and while we should use only algorithms we support, that should be based on HandshakeSettings, not
values in the extension.
This implements Certificate Compression extension and msg (rfc 8879). Unfortunately, I saw that there was already an open pull request #484 addressing the same a little too late. To avoid repetition, I skimmed through the discussion and implemented relevant feedback. The two main differences in the implementation of the above pull request and this one is that:
Certificate
and classCompressedCertificate
separate (instead of the latter being a wrapper around the first)This keeps integration with tlslite-ng relatively simple. To help with the review, here's what the pull request is meant to add:
compress_certificate
extension as a clientCompressedCertificate
msg as a clientcompress_certificate
extension inCertificateRequest
msgCompressedCertificate
msg if the server advertises support for compressionThe server equivalent of the above points are not present yet but can be trivially added after initial review. One area that needs attention is the code for
CompressedCertificate
class, specifically the.create()
anddecompress()
methods (I have added relevant comments explaining the problem). The rfc was a bit vague to me on what exact bytes the encoded compressed certificate msg will have (whether it's type + length + encoded message after decompression or just length + encoded msg), so some changes might be needed there but they should be minimal. For this reason, I have not added tests forCompressedCertificate
msgThis change is