Skip to content

Commit

Permalink
signed_certificate_timestamp extension from RFC 6962
Browse files Browse the repository at this point in the history
  • Loading branch information
tomato42 committed Nov 25, 2016
1 parent d69c210 commit c0211b0
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 1 deletion.
1 change: 1 addition & 0 deletions tlslite/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class ExtensionType(TLSEnum):
srp = 12 # RFC 5054
signature_algorithms = 13 # RFC 5246
alpn = 16 # RFC 7301
signed_certificate_timestamp = 18 # RFC 6962
client_hello_padding = 21 # RFC 7685
encrypt_then_mac = 22 # RFC 7366
extended_master_secret = 23 # RFC 7627
Expand Down
73 changes: 73 additions & 0 deletions tlslite/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,78 @@ def parse(self, parser):
return self


class SCTExtension(TLSExtension):
"""
Client and Server Hello extension from Certificate Transparency.
Extension containing a list of serialised SignedCertificateTimestamp
objects.
See RFC 6962
"""

def __init__(self):
"""Create instance of class"""
extType = ExtensionType.signed_certificate_timestamp
super(SCTExtension, self).__init__(extType=extType)
self.sct_list = None

def create(self, sct_list):
"""
Set the list of signed certificate timestamps
@type sct_list: list of bytearrays
@param sct_list: list of serialised certificate time stamps
"""
self.sct_list = sct_list
return self

@property
def extData(self):
"""
Return raw encoding of the extension
@rtype: bytearray
"""
if self.sct_list is None:
return bytearray(0)

writer = Writer()
# elements have 2 byte header lengths
for sct in self.sct_list:
writer.add(len(sct), 2)
writer.bytes += sct

writer2 = Writer()
writer2.add(len(writer.bytes), 2)
return writer2.bytes + writer.bytes

def parse(self, parser):
"""
Deserialise extension from on the wire data.
@type parser: L{tlslite.util.codec.Parser}
@param parser: data to be parsed
@rtype: L{SCTExtension}
"""
if parser.getRemainingLength() == 0:
self.sct_list = None
return self

self.sct_list = []

parser.startLengthCheck(2)
while not parser.atLengthCheck():
self.sct_list.append(parser.getVarBytes(2))
parser.stopLengthCheck()

if parser.getRemainingLength() != 0:
raise SyntaxError("Trailing data in SCTExtension")

return self


class StatusRequestExtension(TLSExtension):
"""
Handling of the Certificate Status Request extension from RFC 6066.
Expand Down Expand Up @@ -1367,6 +1439,7 @@ def parse(self, parser):
ExtensionType.srp: SRPExtension,
ExtensionType.signature_algorithms: SignatureAlgorithmsExtension,
ExtensionType.alpn: ALPNExtension,
ExtensionType.signed_certificate_timestamp: SCTExtension,
ExtensionType.supports_npn: NPNExtension,
ExtensionType.client_hello_padding: PaddingExtension,
ExtensionType.renegotiation_info: RenegotiationInfoExtension}
Expand Down
87 changes: 86 additions & 1 deletion unit_tests/test_tlslite_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
SRPExtension, ClientCertTypeExtension, ServerCertTypeExtension,\
TACKExtension, SupportedGroupsExtension, ECPointFormatsExtension,\
SignatureAlgorithmsExtension, PaddingExtension, VarListExtension, \
RenegotiationInfoExtension, ALPNExtension, StatusRequestExtension
RenegotiationInfoExtension, ALPNExtension, StatusRequestExtension, \
SCTExtension
from tlslite.utils.codec import Parser
from tlslite.constants import NameType, ExtensionType, GroupName,\
ECPointFormat, HashAlgorithm, SignatureAlgorithm, \
Expand Down Expand Up @@ -1588,6 +1589,90 @@ def test_parse_from_TLSExtension(self):
bytearray(b'spdy/1')])


class TestSCTExtension(unittest.TestCase):
def setUp(self):
self.ext = SCTExtension()

def test___int__(self):
self.assertIsNotNone(self.ext)
self.assertEqual(self.ext.extType, 18)
self.assertEqual(self.ext.extData, bytearray())
self.assertIsNone(self.ext.sct_list)

def test_create(self):
ext2 = self.ext.create([bytearray(b'SCT number 1'),
bytearray(b'SCT number 2')])

self.assertIs(self.ext, ext2)
self.assertEqual(self.ext.sct_list, [bytearray(b'SCT number 1'),
bytearray(b'SCT number 2')])

def test_extData_with_empty_array(self):
self.ext.create([])

self.assertEqual(self.ext.extData, bytearray(b'\x00\x00'))

def test_extData_with_empty_SCTs(self):
self.ext.create([bytearray(), bytearray()])

self.assertEqual(self.ext.extData, bytearray(b'\x00\x04'
b'\x00\x00'
b'\x00\x00'))

def test_extData(self):
self.ext.create([bytearray(b'test'), bytearray(b'example')])

self.assertEqual(self.ext.extData, bytearray(b'\x00\x0f'
b'\x00\x04test'
b'\x00\x07example'))

def test_parse_with_empty_data(self):
parser = Parser(bytearray(b''))

ret = self.ext.parse(parser)

self.assertIs(ret, self.ext)
self.assertIsNone(self.ext.sct_list)

def test_parse_with_empty_array(self):
parser = Parser(bytearray(b'\x00\x00'))

ret = self.ext.parse(parser)

self.assertIs(ret, self.ext)
self.assertEqual(self.ext.sct_list, [])

def test_parse_with_empty_elements(self):
parser = Parser(bytearray(b'\x00\x04\x00\x00\x00\x00'))

self.ext.parse(parser)

self.assertEqual(self.ext.sct_list, [bytearray(), bytearray()])

def test_parse_with_value(self):
parser = Parser(bytearray(b'\x00\x06\x00\x04test'))

self.ext.parse(parser)

self.assertEqual(self.ext.sct_list, [bytearray(b'test')])

def test_parse_with_overflowing_data(self):
parser = Parser(bytearray(b'\x00\x00test'))

with self.assertRaises(SyntaxError):
self.ext.parse(parser)

def test_parse_from_TLSExtension(self):
ext = TLSExtension()

parser = Parser(bytearray(b'\x00\x12\x00\x08'
b'\x00\x06\x00\x04test'))

ret = ext.parse(parser)
self.assertIsInstance(ret, SCTExtension)
self.assertEqual(ret.sct_list, [bytearray(b'test')])


class TestStatusRequestExtension(unittest.TestCase):
def setUp(self):
self.ext = StatusRequestExtension()
Expand Down

0 comments on commit c0211b0

Please sign in to comment.