blob: ef2052f500264b5ec50fff28b408909b8218e96b [file] [log] [blame]
Lucia Lic6ba99d2021-11-08 22:06:11 +08001# 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")