-
Notifications
You must be signed in to change notification settings - Fork 44
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
Parse client_certificate_url
extension and support CertificateURL message
#112
Changes from all commits
9c4e4f5
d43b754
dbcb7db
6fa7c53
8c732bb
f1d59a1
eba6d74
a322860
ca55815
188d7a3
c1917f2
41c8714
346a63b
3aa50d1
4be9ced
3e1758a
17a3280
18c869d
f839d07
e9b6c84
800ad1f
fe03620
3ede2d6
cd8d518
77e451d
1cc0cd4
dd69365
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ Submodules | |
Subpackages | ||
ciphersuites | ||
prepending | ||
subconstruct | ||
subconstructs | ||
versa | ||
prf | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,8 @@ | |
|
||
import six | ||
|
||
from tls.exceptions import TLSValidationException | ||
|
||
|
||
class _UBInt24(construct.Adapter): | ||
def _encode(self, obj, context): | ||
|
@@ -211,6 +213,39 @@ def EnumSwitch(type_field, type_enum, value_field, value_choices, # noqa | |
default=default)) | ||
|
||
|
||
class TLSExprValidator(construct.Validator): | ||
""" | ||
Like :py:class:`construct.ExprValidator`, but raises a | ||
:py:class:`tls.exceptions.TLSValidationException` on validation failure. | ||
|
||
This is necessary because any ConstructError signifies the end of | ||
subconstruct repetition to Range, which in turn breaks use with | ||
``TLSPrefixedArray``. | ||
""" | ||
def __init__(self, subcon, validator): | ||
super(TLSExprValidator, self).__init__(subcon) | ||
self._validate = validator | ||
|
||
def _decode(self, obj, context): | ||
if not self._validate(obj, context): | ||
raise TLSValidationException("object failed validation", obj) | ||
return obj | ||
|
||
|
||
def TLSOneOf(subcon, valids): # noqa | ||
""" | ||
Validates that the object is one of the listed values, both during parsing | ||
and building. Like :py:meth:`construct.OneOf`, but raises a | ||
:py:class:`tls.exceptions.TLSValidationException` instead of a | ||
``ConstructError`` subclass on mismatch. | ||
|
||
This is necessary because any ConstructError signifies the end of | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be nice to put an explanation like this into |
||
subconstruct repetition to Range, which in turn breaks use with | ||
``TLSPrefixedArray``. | ||
""" | ||
return TLSExprValidator(subcon, lambda obj, ctx: obj in valids) | ||
|
||
|
||
class SizeAtLeast(construct.Validator): | ||
""" | ||
A :py:class:`construct.adapter.Validator` that validates a | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,6 +45,8 @@ class HandshakeType(Enum): | |
CERTIFICATE_VERIFY = 15 | ||
CLIENT_KEY_EXCHANGE = 16 | ||
FINISHED = 20 | ||
CERTIFICATE_URL = 21 | ||
CERTIFICATE_STATUS = 22 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think a later PR can make the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes!! I'll play around with that next, blends in well with the #106 branch I was thinking of tackling. |
||
|
||
|
||
class ContentType(Enum): | ||
|
@@ -85,6 +87,10 @@ class AlertDescription(Enum): | |
USER_CANCELED = 90 | ||
NO_RENEGOTIATION = 100 | ||
UNSUPPORTED_EXTENSION = 110 | ||
CERTIFICATE_UNOBTAINABLE = 111 | ||
UNRECOGNIZED_NAME = 112 | ||
BAD_CERTIFICATE_STATUS_RESPONSE = 113 | ||
BAD_CERTIFICATE_HASH_VALUE = 114 | ||
|
||
|
||
class ExtensionType(Enum): | ||
|
@@ -128,3 +134,8 @@ class CompressionMethod(Enum): | |
|
||
class NameType(Enum): | ||
HOST_NAME = 0 | ||
|
||
|
||
class CertChainType(Enum): | ||
INDIVIDUAL_CERTS = 0 | ||
PKIPATH = 1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,12 +12,13 @@ | |
|
||
import pytest | ||
|
||
from tls._common._constructs import (BytesAdapter, EnumClass, | ||
EnumSwitch, Opaque, | ||
PrefixedBytes, SizeAtLeast, | ||
from tls._common._constructs import (BytesAdapter, EnumClass, EnumSwitch, | ||
Opaque, PrefixedBytes, SizeAtLeast, | ||
SizeAtMost, SizeWithin, | ||
TLSPrefixedArray, UBInt24, | ||
_UBInt24) | ||
TLSExprValidator, TLSOneOf, | ||
TLSPrefixedArray, UBInt24, _UBInt24) | ||
|
||
from tls.exceptions import TLSValidationException | ||
|
||
|
||
@pytest.mark.parametrize("byte,number", [ | ||
|
@@ -94,6 +95,111 @@ def test_decode_passes_value_through(self, bytes_adapted, value): | |
assert bytes_adapted._decode(value, context=object()) is value | ||
|
||
|
||
class TestTLSExprValidator(object): | ||
""" | ||
Tests for :py:class:`tls._common._constructs.TLSExprValidator`. | ||
""" | ||
@pytest.fixture | ||
def data_class(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good name - no need for |
||
""" | ||
A :py:func:`construct.macros.UBInt8` construct that requires the | ||
input value to be equal to 6. | ||
""" | ||
return TLSExprValidator(UBInt8('input_byte'), | ||
lambda obj, ctx: obj == 6) | ||
|
||
def test_parse_invalid(self, data_class): | ||
""" | ||
:py:class:`tls.common._constructs.TLSExprValidator` raises a | ||
``TLSValidationException`` when parsing a value that does not | ||
evaluate to the provided expression. | ||
""" | ||
with pytest.raises(TLSValidationException): | ||
data_class.parse(b'\xff') | ||
|
||
def test_parse_valid(self, data_class): | ||
""" | ||
:py:class:`tls.common._constructs.TLSExprValidator` parses a value | ||
that evaluates to the provided expression. | ||
""" | ||
assert data_class.parse(b'\x06') == 6 | ||
|
||
def test_build_invalid(self, data_class): | ||
""" | ||
:py:class:`tls.common._constructs.TLSExprValidator` raises a | ||
``TLSValidationException`` when serializing a value that does not | ||
evaluate to the provided expression. | ||
""" | ||
with pytest.raises(TLSValidationException): | ||
data_class.build(2) | ||
|
||
def test_build_valid(self, data_class): | ||
""" | ||
:py:class:`tls.common._construct.TLSExprValidator` successfully | ||
serializes a value into bytes when it evaluates to the provided | ||
expression. | ||
""" | ||
assert data_class.build(6) == b'\x06' | ||
|
||
|
||
class TestTLSOneOf(object): | ||
""" | ||
Tests for :py:meth:`tls._common._constructs.TLSOneOf`. | ||
""" | ||
|
||
@pytest.fixture | ||
def data_class(self): | ||
""" | ||
A :py:func:`construct.macros.UBInt8` construct that requires the | ||
input value to be equal to one of 1, 3, or 5. | ||
""" | ||
return TLSOneOf(UBInt8('input'), | ||
[1, 3, 5]) | ||
|
||
def test_parse_invalid(self, data_class): | ||
""" | ||
:py:meth:`tls.common._constructs.TLSOneOf` raises a | ||
``TLSValidationException`` when parsing a value that is not one of | ||
the values in the provided list. | ||
""" | ||
with pytest.raises(TLSValidationException): | ||
data_class.parse(b'\xff') | ||
|
||
@pytest.mark.parametrize('input_bytes,parsed_output', [ | ||
(b'\x01', 1), | ||
(b'\x03', 3), | ||
(b'\x05', 5), | ||
]) | ||
def test_parse_valid(self, data_class, input_bytes, parsed_output): | ||
""" | ||
:py:meth:`tls.common._constructs.TLSOneOf` parses a value that | ||
equals one of the values in the provided list. | ||
""" | ||
assert data_class.parse(input_bytes) == parsed_output | ||
|
||
def test_build_invalid(self, data_class): | ||
""" | ||
:py:meth:`tls.common._constructs.TLSOneOf` raises a | ||
``TLSValidationException`` when serializing a value that is not one | ||
of the values in the provided list. | ||
""" | ||
with pytest.raises(TLSValidationException): | ||
data_class.build(2) | ||
|
||
@pytest.mark.parametrize('input,built_bytes', [ | ||
(1, b'\x01'), | ||
(3, b'\x03'), | ||
(5, b'\x05'), | ||
]) | ||
def test_build_valid(self, data_class, input, built_bytes): | ||
""" | ||
:py:meth:`tls.common._construct.TLSOneOf` successfully serializes a | ||
value into bytes when it evaluates to one of the values in the | ||
provided list. | ||
""" | ||
assert data_class.build(input) == built_bytes | ||
|
||
|
||
@pytest.mark.parametrize("bytestring,encoded", [ | ||
(b"", b"\x00" + b""), | ||
(b"some value", b"\x0A" + b"some value"), | ||
|
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.
This is a great way to implement
TLSOneOf
! We can reuseTLSExprValidator
for other constructs. Thanks!!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.
:)
Took this idea from
construct
'sOneOf
.