Repository navigation
Expand file tree
/
Copy pathVerifyCertificateTest.php
More file actions
301 lines (284 loc) · 8.59 KB
/
Copy pathVerifyCertificateTest.php
File metadata and controls
301 lines (284 loc) · 8.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
<?php
namespace WpOrg\Requests\Tests\Ssl;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Ssl;
use WpOrg\Requests\Tests\Ssl\SslTestCase;
use WpOrg\Requests\Tests\TypeProviderHelper;
/**
* @covers \WpOrg\Requests\Ssl::verify_certificate
*/
final class VerifyCertificateTest extends SslTestCase {
/**
* Tests receiving an exception when an invalid input type is passed as $host.
*
* @dataProvider dataInvalidInputHost
*
* @param mixed $input Input data.
*
* @return void
*/
public function testInvalidInputHost($input) {
$this->expectException(InvalidArgument::class);
$this->expectExceptionMessage('Argument #1 ($host) must be of type string|Stringable');
Ssl::verify_certificate($input, []);
}
/**
* Data Provider.
*
* @return array
*/
public static function dataInvalidInputHost() {
return TypeProviderHelper::getAllExcept(TypeProviderHelper::GROUP_STRINGABLE);
}
/**
* Tests receiving an exception when an invalid input type is passed as $cert.
*
* @dataProvider dataInvalidInputCert
*
* @param mixed $input Input data.
*
* @return void
*/
public function testInvalidInputCert($input) {
$this->expectException(InvalidArgument::class);
$this->expectExceptionMessage('Argument #2 ($cert) must be of type array|ArrayAccess');
Ssl::verify_certificate('host', $input);
}
/**
* Data Provider.
*
* @return array
*/
public static function dataInvalidInputCert() {
return TypeProviderHelper::getAllExcept(TypeProviderHelper::GROUP_ARRAY_ACCESSIBLE);
}
/**
* Test handling of matching host and DNS names based on certificate.
*
* @dataProvider dataMatch
* @dataProvider dataMatchViaCertificate
*
* @param string $host Host name to verify.
* @param string $reference DNS name to match against.
* @param bool|string $with_san Optional. How to generate the fake certificate.
* - false: plain, CN only;
* - true: CN + subjectAltName, alt set to same as CN;
* - string: CN + subjectAltName, alt set to string value.
*
* @return void
*/
public function testMatchViaCertificate($host, $reference, $with_san = true) {
$certificate = $this->fakeCertificate($reference, $with_san);
$this->assertTrue(Ssl::verify_certificate($host, $certificate));
}
/**
* Data provider for additional test cases specific to the Ssl::verify_certificate() method.
*
* @return array
*/
public static function dataMatchViaCertificate() {
return [
'top-level domain; missing SAN, fallback to CN' => [
'host' => 'example.com',
'reference' => 'example.com',
'with_san' => false,
],
'SAN available, multiple alternatives provided; matching is second' => [
'host' => 'example.net',
'reference' => 'example.net',
'with_san' => 'DNS: example.com, DNS: example.net, DNS: example.info',
],
'SAN available, multiple alternatives provided; matching is last' => [
'host' => 'example.net',
'reference' => 'example.net',
'with_san' => 'DNS: example.com, DNS: example.org, DNS: example.net',
],
'SAN available, DNS prefix missing in first, not (matching) second' => [
'host' => 'example.net',
'reference' => 'example.com',
'with_san' => 'example.com, DNS: example.net',
],
'SAN available, DNS prefix missing in all, fallback to CN' => [
'host' => 'example.net',
'reference' => 'example.net',
'with_san' => 'example.com, example.net',
],
];
}
/**
* Test handling of non-matching host and DNS names based on certificate.
*
* @dataProvider dataNoMatch
* @dataProvider dataNoMatchViaCertificate
*
* @param string $host Host name to verify.
* @param string $reference DNS name to match against.
* @param bool|string $with_san Optional. How to generate the fake certificate.
* - false: plain, CN only;
* - true: CN + subjectAltName, alt set to same as CN;
* - string: CN + subjectAltName, alt set to string value.
*
* @return void
*/
public function testNoMatchViaCertificate($host, $reference, $with_san = true) {
$certificate = $this->fakeCertificate($reference, $with_san);
$this->assertFalse(Ssl::verify_certificate($host, $certificate));
}
/**
* Data provider for additional test cases specific to the Ssl::verify_certificate() method.
*
* @return array
*/
public static function dataNoMatchViaCertificate() {
return [
'top-level domain; missing SAN, fallback to invalid CN' => [
'host' => 'example.net',
'reference' => 'example.com',
'with_san' => false,
],
'SAN empty' => [
'host' => 'example.net',
'reference' => 'example.com',
'with_san' => '',
],
'SAN available, DNS prefix missing' => [
'host' => 'example.net',
'reference' => 'example.com',
'with_san' => 'example.com',
],
'SAN available, multiple alternatives provided; none of the SANs match, DNS prefix missing in first, not second' => [
'host' => 'example.net',
'reference' => 'example.com',
'with_san' => 'example.com, DNS: example.org',
],
'SAN available, multiple alternatives provided; none of the SANs match, even though CN does' => [
'host' => 'example.net',
'reference' => 'example.net',
'with_san' => 'DNS: example.com, DNS: example.org, DNS: example.info',
],
];
}
/**
* Test a certificate with both CN and SAN fields
*
* As per RFC2818, if the SAN field exists, we should parse that and ignore
* the value of the CN field.
*
* @link https://tools.ietf.org/html/rfc2818#section-3.1
*/
public function testIgnoreCNWithSAN() {
$certificate = $this->fakeCertificate('example.net', 'DNS: example.com');
$this->assertTrue(Ssl::verify_certificate('example.com', $certificate), 'Checking SAN validation');
$this->assertFalse(Ssl::verify_certificate('example.net', $certificate), 'Checking CN non-validation');
}
/**
* Test handling of non-compliant certificates.
*
* @dataProvider dataWithInvalidCertificates
*
* @param string $host Host name to verify.
* @param array $certificate A (faked) certificate to verify against.
* @param bool $expected Expected function output.
*
* @return void
*/
public function testWithInvalidCertificates($host, $certificate, $expected) {
$this->assertSame($expected, Ssl::verify_certificate($host, $certificate));
}
/**
* Data provider.
*
* @return array
*/
public static function dataWithInvalidCertificates() {
return [
'empty array' => [
'host' => 'example.com',
'certificate' => [],
'expected' => false,
],
'subject, but missing CN entry; no SAN' => [
'host' => 'example.com',
'certificate' => [
'subject' => [],
],
'expected' => false,
],
'subject with empty CN entry; no SAN' => [
'host' => 'example.com',
'certificate' => [
'subject' => [
'CN' => '',
],
],
'expected' => false,
],
'subject, but missing CN entry; SAN exists, missing DNS' => [
'host' => 'example.com',
'certificate' => [
'subject' => [],
'extensions' => [
'subjectAltName' => 'example.net',
],
],
'expected' => false,
],
'subject with empty CN entry; SAN exists, missing DNS' => [
'host' => 'example.com',
'certificate' => [
'subject' => [
'CN' => '',
],
'extensions' => [
'subjectAltName' => 'example.net',
],
],
'expected' => false,
],
'subject, but missing CN entry; SAN exists, but non-matching' => [
'host' => 'example.com',
'certificate' => [
'subject' => [],
'extensions' => [
'subjectAltName' => 'DNS: example.net',
],
],
'expected' => false,
],
'subject with empty CN entry; SAN exists, but non-matching' => [
'host' => 'example.com',
'certificate' => [
'subject' => [
'CN' => '',
],
'extensions' => [
'subjectAltName' => 'DNS: example.net',
],
],
'expected' => false,
],
'extensions, but missing SAN entry' => [
'host' => 'example.com',
'certificate' => [
'subject' => [
'CN' => 'example.net',
],
'extensions' => [],
],
'expected' => false,
],
'extensions with empty SAN entry' => [
'host' => 'example.com',
'certificate' => [
'subject' => [
'CN' => 'example.net',
],
'extensions' => [
'subjectAltName' => '',
],
],
'expected' => false,
],
];
}
}