1# This file is dual licensed under the terms of the Apache License, Version 2# 2.0, and the BSD License. See the LICENSE file in the root of this repository 3# for complete details. 4 5from __future__ import absolute_import, division, print_function 6 7import pytest 8 9from cryptography.hazmat._der import ( 10 DERReader, 11 INTEGER, 12 NULL, 13 OCTET_STRING, 14 SEQUENCE, 15 encode_der, 16 encode_der_integer, 17) 18 19 20def test_der_reader_basic(): 21 reader = DERReader(b"123456789") 22 assert reader.read_byte() == ord(b"1") 23 assert reader.read_bytes(1).tobytes() == b"2" 24 assert reader.read_bytes(4).tobytes() == b"3456" 25 26 with pytest.raises(ValueError): 27 reader.read_bytes(4) 28 29 assert reader.read_bytes(3).tobytes() == b"789" 30 31 # The input is now empty. 32 with pytest.raises(ValueError): 33 reader.read_bytes(1) 34 with pytest.raises(ValueError): 35 reader.read_byte() 36 37 38def test_der(): 39 # This input is the following structure, using 40 # https://github.com/google/der-ascii 41 # 42 # SEQUENCE { 43 # SEQUENCE { 44 # NULL {} 45 # INTEGER { 42 } 46 # OCTET_STRING { "hello" } 47 # } 48 # } 49 der = b"\x30\x0e\x30\x0c\x05\x00\x02\x01\x2a\x04\x05\x68\x65\x6c\x6c\x6f" 50 reader = DERReader(der) 51 with pytest.raises(ValueError): 52 reader.check_empty() 53 54 with pytest.raises(ValueError): 55 with reader: 56 pass 57 58 with pytest.raises(ZeroDivisionError): 59 with DERReader(der): 60 raise ZeroDivisionError 61 62 # Parse the outer element. 63 outer = reader.read_element(SEQUENCE) 64 reader.check_empty() 65 assert outer.data.tobytes() == der[2:] 66 67 # Parse the outer element with read_any_element. 68 reader = DERReader(der) 69 tag, outer2 = reader.read_any_element() 70 reader.check_empty() 71 assert tag == SEQUENCE 72 assert outer2.data.tobytes() == der[2:] 73 74 # Parse the outer element with read_single_element. 75 outer3 = DERReader(der).read_single_element(SEQUENCE) 76 assert outer3.data.tobytes() == der[2:] 77 78 # read_single_element rejects trailing data. 79 with pytest.raises(ValueError): 80 DERReader(der + der).read_single_element(SEQUENCE) 81 82 # Continue parsing the structure. 83 inner = outer.read_element(SEQUENCE) 84 outer.check_empty() 85 86 # Parsing a missing optional element should work. 87 assert inner.read_optional_element(INTEGER) is None 88 89 null = inner.read_element(NULL) 90 null.check_empty() 91 92 # Parsing a present optional element should work. 93 integer = inner.read_optional_element(INTEGER) 94 assert integer.as_integer() == 42 95 96 octet_string = inner.read_element(OCTET_STRING) 97 assert octet_string.data.tobytes() == b"hello" 98 99 # Parsing a missing optional element should work when the input is empty. 100 inner.check_empty() 101 assert inner.read_optional_element(INTEGER) is None 102 103 # Re-encode the same structure. 104 der2 = encode_der( 105 SEQUENCE, 106 encode_der( 107 SEQUENCE, 108 encode_der(NULL), 109 encode_der(INTEGER, encode_der_integer(42)), 110 encode_der(OCTET_STRING, b"hello"), 111 ), 112 ) 113 assert der2 == der 114 115 116@pytest.mark.parametrize( 117 "length,header", 118 [ 119 # Single-byte lengths. 120 (0, b"\x04\x00"), 121 (1, b"\x04\x01"), 122 (2, b"\x04\x02"), 123 (127, b"\x04\x7f"), 124 # Long-form lengths. 125 (128, b"\x04\x81\x80"), 126 (129, b"\x04\x81\x81"), 127 (255, b"\x04\x81\xff"), 128 (0x100, b"\x04\x82\x01\x00"), 129 (0x101, b"\x04\x82\x01\x01"), 130 (0xFFFF, b"\x04\x82\xff\xff"), 131 (0x10000, b"\x04\x83\x01\x00\x00"), 132 ], 133) 134def test_der_lengths(length, header): 135 body = length * b"a" 136 der = header + body 137 138 reader = DERReader(der) 139 element = reader.read_element(OCTET_STRING) 140 reader.check_empty() 141 assert element.data.tobytes() == body 142 143 assert encode_der(OCTET_STRING, body) == der 144 145 146@pytest.mark.parametrize( 147 "bad_input", 148 [ 149 # The input ended before the tag. 150 b"", 151 # The input ended before the length. 152 b"\x30", 153 # The input ended before the second byte of the length. 154 b"\x30\x81", 155 # The input ended before the body. 156 b"\x30\x01", 157 # The length used long form when it should be short form. 158 b"\x30\x81\x01\x00", 159 # The length was not minimally-encoded. 160 b"\x30\x82\x00\x80" + (0x80 * b"a"), 161 # Indefinite-length encoding is not valid DER. 162 b"\x30\x80\x00\x00" 163 # Tag number (the bottom 5 bits) 31 indicates long form tags, which we 164 # do not support. 165 b"\x1f\x00", 166 b"\x9f\x00", 167 b"\xbf\x00", 168 b"\xff\x00", 169 ], 170) 171def test_der_reader_bad_input(bad_input): 172 reader = DERReader(bad_input) 173 with pytest.raises(ValueError): 174 reader.read_any_element() 175 176 177def test_der_reader_wrong_tag(): 178 reader = DERReader(b"\x04\x00") 179 with pytest.raises(ValueError): 180 reader.read_element(SEQUENCE) 181 182 183@pytest.mark.parametrize( 184 "value,der", 185 [ 186 (0, b"\x00"), 187 (1, b"\x01"), 188 (2, b"\x02"), 189 (3, b"\x03"), 190 (127, b"\x7f"), 191 (128, b"\x00\x80"), 192 ( 193 0x112233445566778899AABBCCDDEEFF, 194 b"\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff", 195 ), 196 ], 197) 198def test_integer(value, der): 199 assert encode_der_integer(value) == der 200 assert DERReader(der).as_integer() == value 201 202 203@pytest.mark.parametrize( 204 "bad_input", 205 [ 206 # Zero is encoded as b"\x00", not the empty string. 207 b"", 208 # Too many leading zeros. 209 b"\x00\x00", 210 b"\x00\x7f", 211 # Too many leading ones. 212 b"\xff\xff", 213 b"\xff\x80", 214 # Negative integers are not supported. 215 b"\x80", 216 b"\x81", 217 b"\x80\x00\x00", 218 b"\xff", 219 ], 220) 221def test_invalid_integer(bad_input): 222 reader = DERReader(bad_input) 223 with pytest.raises(ValueError): 224 reader.as_integer() 225 226 227def test_invalid_integer_encode(): 228 with pytest.raises(ValueError): 229 encode_der_integer(-1) 230 231 with pytest.raises(ValueError): 232 encode_der_integer("not an integer") 233