def test_sequence_custom_builder(self): """Test custom builder in Decoder.read_sequence""" class TracingSequenceDecodingBuilder(SequenceDecodingBuilder): def __init__(self, *args, **kwargs): super(TracingSequenceDecodingBuilder, self).__init__(*args, **kwargs) self.trace = [] def begin_sequence(self): super(TracingSequenceDecodingBuilder, self).begin_sequence() self.trace.append('[') def end_sequence(self): super(TracingSequenceDecodingBuilder, self).end_sequence() self.trace.append(']') def handle(self, tag): data = super(TracingSequenceDecodingBuilder, self).handle(tag) self.trace.append((tag, data)) return data # taken from test_sequence (see above) bio = BytesIO( # noqa: E131 b'\x30\x80' # noqa: E131 b'\x30\x0F' # noqa: E131 b'\x30\x0D' # noqa: E131 b'\x05\x00' b'\x04\x03foo' b'\x30\x80' # noqa: E131 b'\x05\x00' b'\x00\x00' b'\x00\x00') dec = Decoder(bio) builder = TracingSequenceDecodingBuilder(dec) self.assertEqual([[[None, b'foo', [None]]]], dec.read_sequence(builder)) expected_trace = [ # noqa: E131 '[', # noqa: E131 '[', # noqa: E131 '[', # noqa: E131 (Tag.NULL, None), (Tag.OCTETSTRING_PRIMITIVE, b'foo'), '[', # noqa: E131 (Tag.NULL, None), ']', ']', ']', ']', ] self.assertEqual(expected_trace, builder.trace)
def test_octetstring_constructed(self): """Decode an octetstring (constructed)""" bio = BytesIO( # noqa: E131 b'\x24\x80' # noqa: E131 b'\x24\x80' b'\x04\x0A0123456789' b'\x00\x00' b'\x24\x80' # noqa: E131 b'\x04\x03foo' b'\x04\x06\x03\x04\x02\xEA\xAF\x4C' b'\x24\x80' # noqa: E131 b'\x04\x05abcde' b'\x24\x80' b'\x00\x00' b'\x24\x80' b'\x00\x00' b'\x04\x02\x00\x00' b'\x00\x00' b'\x00\x00' b'\x24\x80' b'\x24\x80' b'\x00\x00' b'\x00\x00' b'\x24\x80' b'\x00\x00' b'\x00\x00') dec = Decoder(bio) self.assertEqual(b'0123456789foo\x03\x04\x02\xEA\xAF\x4Cabcde\x00\x00', dec.read_octetstring())
def _assert_tag(self, *tag): bio = BytesIO() enc = Encoder(bio) enc.write_tag(*tag) bio.seek(0, os.SEEK_SET) dec = Decoder(bio) actual_tag = dec.read_tag() self.assertEqual(tag, actual_tag)
def test_boolean(self): """Encode and decode booleans""" self._assert_boolean(True) self._assert_boolean(False) bio = BytesIO() enc = Encoder(bio) enc.write_boolean(42) self.assertEqual(b'\x01\x01\xff', bio.getvalue()) dec = Decoder(BytesIO(b'\x01\x01\x01')) self.assertTrue(dec.read_boolean())
def test_printablestring(self): """Encode and decode printable string values""" self._assert_printablestring('') self._assert_printablestring('foo bar 1337') self._assert_printablestring('Aa09 \'()+,-./:=?') # The following examples are adopted from X.690 (see around 8.23.6) # primitive form bio = BytesIO(b'\x13\x05\x4A\x6F\x6E\x65\x73') dec = Decoder(bio) self.assertEqual('Jones', dec.read_printablestring()) # constructor form, definite length bio = BytesIO( # noqa: E131 b'\x33\x09' # noqa: E131 b'\x04\x03\x4A\x6F\x6E' b'\x04\x02\x65\x73') dec = Decoder(bio) self.assertEqual('Jones', dec.read_printablestring()) # constructor form, indefinite length bio = BytesIO( # noqa: E131 b'\x33\x80' # noqa: E131 b'\x04\x03\x4A\x6F\x6E' b'\x04\x02\x65\x73' b'\x00\x00') dec = Decoder(bio) self.assertEqual('Jones', dec.read_printablestring()) # test encoding bio = BytesIO() enc = Encoder(bio) enc.write_printablestring('Fo0') self.assertEqual(b'\x13\x03\x46\x6F\x30', bio.getvalue())
def test_utctime(self): """Encode and decode utctime values""" self._assert_utctime('920622123421Z') self._assert_utctime('210526000000Z') # invalid dates are supported (february does not have 31 days) self._assert_utctime('210231000000Z') bio = BytesIO(b'\x17\x0D001231235959Z') dec = Decoder(bio) self.assertEqual('001231235959Z', dec.read_utctime()) # encode bio = BytesIO() enc = Encoder(bio) enc.write_utctime('990101000000Z') self.assertEqual(b'\x17\x0D990101000000Z', bio.getvalue())
def test_printablestring_errors(self): """Try to encode/decode non-printable strings""" # newline '\n' is not allowed bio = BytesIO(b'\x13\x01\x0A') dec = Decoder(bio) with self.assertRaises(DecodingError): print(dec.read_printablestring().encode()) # encode bio = BytesIO() enc = Encoder(bio) with self.assertRaises(ValueError): enc.write_printablestring('foo\0bar') with self.assertRaises(ValueError): enc.write_printablestring('foo\nbar')
def test_length(self): """Encode and decode various lengths""" self._assert_length(0) self._assert_length(42) self._assert_length(127) self._assert_length(128) self._assert_length(255) self._assert_length(256) self._assert_length(1337) self._assert_length(0xdeadbeef) self._assert_length(18345736593467563457631984) # indefinite test bio = BytesIO() enc = Encoder(bio) enc.write_indefinite_length() self.assertEqual(b'\x80', bio.getvalue()) dec = Decoder(BytesIO(b'\x80')) self.assertEqual(-1, dec.read_length())
def _assert_function(self, what, data): bio = BytesIO() enc = Encoder(bio) enc_meth = getattr(enc, "write_{}".format(what)) enc_meth(data) bio.seek(0, os.SEEK_SET) dec = Decoder(bio) dec_meth = getattr(dec, "read_{}".format(what)) actual_data = dec_meth() self.assertEqual(data, actual_data) self._assert_empty_bio(bio)
def test_utf8string(self): """Encode and decode utf8 string values""" self._assert_utf8string('') self._assert_utf8string('\0\n\r\n\t') self._assert_utf8string('foo\nbar\0baz') self._assert_utf8string('teSt \xFF \x00 \x61 4711') bio = BytesIO(b'\x0C\x05\x66\x6F\x6F\xC3\xBF') dec = Decoder(bio) self.assertEqual('foo\xff', dec.read_utf8string()) # as above but split the encoding of 0xFF into two octets bio = BytesIO( # noqa: E131 b'\x2C\x0D' # noqa: E131 b'\x04\x01\x66' # f b'\x04\x02\x6F\x6F' # oo b'\x04\x01\xC3' # first part of 0xFF's encoding b'\x04\x01\xBF' # second part of 0xFF's encoding ) dec = Decoder(bio) self.assertEqual('foo\xff', dec.read_utf8string()) # encode bio = BytesIO() enc = Encoder(bio) enc.write_utf8string('foo\nbar') self.assertEqual(b'\x0C\x07\x66\x6F\x6F\x0A\x62\x61\x72', bio.getvalue())
def test_utf8string_errors(self): """Try to decode an invalid utf8 encoding""" bio = BytesIO(b'\x0C\x01\x61\x0C\x01\xFF\x0C\x01\x62\x0C\x01\x63') dec = Decoder(bio) self.assertEqual('a', dec.read_utf8string()) with self.assertRaises(UnicodeDecodeError): dec.read_utf8string() self.assertEqual('b', dec.read_utf8string()) self.assertEqual('c', dec.read_utf8string())
def test_bitstring_no_unused_bits(self): """Decode a non-empty bitstring with no unused bits""" bio = BytesIO(b'\x03\x04\x00\x00\x00\xff') dec = Decoder(bio) raw = bytearray([0x00, 0x00, 0xFF]) self.assertEqual(raw, dec.read_bitstring()) bio = BytesIO(b'\x03\x04\x00\x00\x00\x00') dec = Decoder(bio) raw = bytearray([0x00, 0x00, 0x00]) self.assertEqual(raw, dec.read_bitstring())
def test_bitstring_constructed(self): """Decode a bitstring (constructed)""" # see X.690 (8.6.4.2) raw = bytearray([0x0A, 0x3B, 0x5F, 0x29, 0x1C, 0xD0]) bio = BytesIO( b'\x23\x80\x03\x03\x00\x0A\x3B\x03\x05\x04\x5F\x29\x1C\xD0\x00\x00' ) dec = Decoder(bio) self.assertEqual(raw, dec.read_bitstring()) # nested encoding bio = BytesIO( # noqa: E131 b'\x23\x80' # noqa: E131 b'\x23\x80' # noqa: E131 b'\x23\x80' # noqa: E131 b'\x23\x80' # noqa: E131 b'\x03\x01\x00' # empty bitstring b'\x03\x04\01\xAF\x00\x3E' b'\x03\x02\x00\xFF' b'\x00\x00' b'\x03\x03\x07\x11\x80' b'\x00\x00' b'\x23\x80' b'\x03\x02\x00\x0F' b'\x00\x00' b'\x00\x00' b'\x23\x80' b'\x00\x00' b'\x23\x80' b'\x03\x01\x00' # empty bitstring b'\x00\x00' b'\x03\x04\x02\xEA\xAF\x4C' b'\x00\x00') dec = Decoder(bio) self.assertEqual(b'\xAF\x00\x3E\xFF\x11\x80\x0F\xEA\xAF\x4C', dec.read_bitstring())
def test_sequence(self): """Encode and decode sequence values""" self._assert_sequence([]) self._assert_sequence([ 4, 5, True, b'an octet string', [], [b'', 42, 1337, [[], [], [[[]]]]], [4711] ]) bio = BytesIO() enc = Encoder(bio) enc.write_sequence([b'foo', [], [True, [], [None, False, b'x']], None]) self.assertEqual( # noqa: E131 b'\x30\x1A' # noqa: E131 b'\x04\x03foo' b'\x30\x00' b'\x30\x0F' # noqa: E131 b'\x01\x01\xFF' b'\x30\x00' b'\x30\x08' # noqa: E131 b'\x05\x00' b'\x01\x01\x00' b'\x04\x01x' b'\x05\x00', bio.getvalue()) # indefinite length bio = BytesIO(b'\x30\x80\x04\x03foo\x30\x80\x00\x00\x05\x00\x00\x00') dec = Decoder(bio) self.assertEqual([b'foo', [], None], dec.read_sequence()) # mix indefinite + definite lengths bio = BytesIO( # noqa: E131 b'\x30\x80' # noqa: E131 b'\x30\x0F' # noqa: E131 b'\x30\x0D' # noqa: E131 b'\x05\x00' b'\x04\x03foo' b'\x30\x80' # noqa: E131 b'\x05\x00' b'\x00\x00' b'\x00\x00') dec = Decoder(bio) self.assertEqual([[[None, b'foo', [None]]]], dec.read_sequence()) # "excess" end of contents is not considered bio = BytesIO(b'\x30\x80\x00\x00\x00\x00') dec = Decoder(bio) self.assertEqual([], dec.read_sequence()) dec.read_end_of_contents()
def test_bitstring_unused_bits_errors(self): """Error out on illegal unused bit values""" # 7 bits should be unused but the last bit is set bio = BytesIO(b'\x03\x06\x07\x00\x00\x01\x00\x01') dec = Decoder(bio) with self.assertRaises(DecodingError): dec.read_bitstring() # 7 bits should be unused but bit 4 (from lsb to msb) is set bio = BytesIO(b'\x03\x06\x07\x00\x00\x01\x00\x10') dec = Decoder(bio) with self.assertRaises(DecodingError): dec.read_bitstring() # the last 3 bits should be unset bio = BytesIO(b'\x03\x06\x03\x00\x00\x01\x00\x14') dec = Decoder(bio) with self.assertRaises(DecodingError): dec.read_bitstring()
def test_bitstring_max_unused_bits(self): """Decode a non-empty bitstring with seven unused bits""" bio = BytesIO(b'\x03\x06\x07\x00\x00\x01\x00\x00') dec = Decoder(bio) raw = bytearray([0x00, 0x00, 0x01, 0x00, 0x00]) self.assertEqual(raw, dec.read_bitstring())