1 # coding=utf-8 |
1 # coding=utf-8 |
2 from __future__ import absolute_import, print_function |
2 from __future__ import absolute_import, print_function |
3 |
3 |
4 import doctest |
4 import doctest |
5 import os |
5 import os |
|
6 |
6 |
7 |
7 def check(a, b): |
8 def check(a, b): |
8 if a != b: |
9 if a != b: |
9 print((a, b)) |
10 print((a, b)) |
10 |
11 |
|
12 |
11 def cert(cn): |
13 def cert(cn): |
12 return {'subject': ((('commonName', cn),),)} |
14 return {'subject': ((('commonName', cn),),)} |
13 |
15 |
14 from mercurial import ( |
16 |
15 sslutil, |
17 from mercurial import sslutil |
16 ) |
|
17 |
18 |
18 _verifycert = sslutil._verifycert |
19 _verifycert = sslutil._verifycert |
19 # Test non-wildcard certificates |
20 # Test non-wildcard certificates |
20 check(_verifycert(cert('example.com'), 'example.com'), |
21 check(_verifycert(cert('example.com'), 'example.com'), None) |
21 None) |
22 check( |
22 check(_verifycert(cert('example.com'), 'www.example.com'), |
23 _verifycert(cert('example.com'), 'www.example.com'), |
23 b'certificate is for example.com') |
24 b'certificate is for example.com', |
24 check(_verifycert(cert('www.example.com'), 'example.com'), |
25 ) |
25 b'certificate is for www.example.com') |
26 check( |
|
27 _verifycert(cert('www.example.com'), 'example.com'), |
|
28 b'certificate is for www.example.com', |
|
29 ) |
26 |
30 |
27 # Test wildcard certificates |
31 # Test wildcard certificates |
28 check(_verifycert(cert('*.example.com'), 'www.example.com'), |
32 check(_verifycert(cert('*.example.com'), 'www.example.com'), None) |
29 None) |
33 check( |
30 check(_verifycert(cert('*.example.com'), 'example.com'), |
34 _verifycert(cert('*.example.com'), 'example.com'), |
31 b'certificate is for *.example.com') |
35 b'certificate is for *.example.com', |
32 check(_verifycert(cert('*.example.com'), 'w.w.example.com'), |
36 ) |
33 b'certificate is for *.example.com') |
37 check( |
|
38 _verifycert(cert('*.example.com'), 'w.w.example.com'), |
|
39 b'certificate is for *.example.com', |
|
40 ) |
34 |
41 |
35 # Test subjectAltName |
42 # Test subjectAltName |
36 san_cert = {'subject': ((('commonName', 'example.com'),),), |
43 san_cert = { |
37 'subjectAltName': (('DNS', '*.example.net'), |
44 'subject': ((('commonName', 'example.com'),),), |
38 ('DNS', 'example.net'))} |
45 'subjectAltName': (('DNS', '*.example.net'), ('DNS', 'example.net')), |
39 check(_verifycert(san_cert, 'example.net'), |
46 } |
40 None) |
47 check(_verifycert(san_cert, 'example.net'), None) |
41 check(_verifycert(san_cert, 'foo.example.net'), |
48 check(_verifycert(san_cert, 'foo.example.net'), None) |
42 None) |
|
43 # no fallback to subject commonName when subjectAltName has DNS |
49 # no fallback to subject commonName when subjectAltName has DNS |
44 check(_verifycert(san_cert, 'example.com'), |
50 check( |
45 b'certificate is for *.example.net, example.net') |
51 _verifycert(san_cert, 'example.com'), |
|
52 b'certificate is for *.example.net, example.net', |
|
53 ) |
46 # fallback to subject commonName when no DNS in subjectAltName |
54 # fallback to subject commonName when no DNS in subjectAltName |
47 san_cert = {'subject': ((('commonName', 'example.com'),),), |
55 san_cert = { |
48 'subjectAltName': (('IP Address', '8.8.8.8'),)} |
56 'subject': ((('commonName', 'example.com'),),), |
|
57 'subjectAltName': (('IP Address', '8.8.8.8'),), |
|
58 } |
49 check(_verifycert(san_cert, 'example.com'), None) |
59 check(_verifycert(san_cert, 'example.com'), None) |
50 |
60 |
51 # Avoid some pitfalls |
61 # Avoid some pitfalls |
52 check(_verifycert(cert('*.foo'), 'foo'), |
62 check(_verifycert(cert('*.foo'), 'foo'), b'certificate is for *.foo') |
53 b'certificate is for *.foo') |
|
54 check(_verifycert(cert('*o'), 'foo'), None) |
63 check(_verifycert(cert('*o'), 'foo'), None) |
55 |
64 |
56 check(_verifycert({'subject': ()}, |
65 check( |
57 'example.com'), |
66 _verifycert({'subject': ()}, 'example.com'), |
58 b'no commonName or subjectAltName found in certificate') |
67 b'no commonName or subjectAltName found in certificate', |
59 check(_verifycert(None, 'example.com'), |
68 ) |
60 b'no certificate received') |
69 check(_verifycert(None, 'example.com'), b'no certificate received') |
61 |
70 |
62 # Unicode (IDN) certname isn't supported |
71 # Unicode (IDN) certname isn't supported |
63 check(_verifycert(cert(u'\u4f8b.jp'), 'example.jp'), |
72 check( |
64 b'IDN in certificate not supported') |
73 _verifycert(cert(u'\u4f8b.jp'), 'example.jp'), |
|
74 b'IDN in certificate not supported', |
|
75 ) |
65 |
76 |
66 # The following tests are from CPython's test_ssl.py. |
77 # The following tests are from CPython's test_ssl.py. |
67 check(_verifycert(cert('example.com'), 'example.com'), None) |
78 check(_verifycert(cert('example.com'), 'example.com'), None) |
68 check(_verifycert(cert('example.com'), 'ExAmple.cOm'), None) |
79 check(_verifycert(cert('example.com'), 'ExAmple.cOm'), None) |
69 check(_verifycert(cert('example.com'), 'www.example.com'), |
80 check( |
70 b'certificate is for example.com') |
81 _verifycert(cert('example.com'), 'www.example.com'), |
71 check(_verifycert(cert('example.com'), '.example.com'), |
82 b'certificate is for example.com', |
72 b'certificate is for example.com') |
83 ) |
73 check(_verifycert(cert('example.com'), 'example.org'), |
84 check( |
74 b'certificate is for example.com') |
85 _verifycert(cert('example.com'), '.example.com'), |
75 check(_verifycert(cert('example.com'), 'exampleXcom'), |
86 b'certificate is for example.com', |
76 b'certificate is for example.com') |
87 ) |
|
88 check( |
|
89 _verifycert(cert('example.com'), 'example.org'), |
|
90 b'certificate is for example.com', |
|
91 ) |
|
92 check( |
|
93 _verifycert(cert('example.com'), 'exampleXcom'), |
|
94 b'certificate is for example.com', |
|
95 ) |
77 check(_verifycert(cert('*.a.com'), 'foo.a.com'), None) |
96 check(_verifycert(cert('*.a.com'), 'foo.a.com'), None) |
78 check(_verifycert(cert('*.a.com'), 'bar.foo.a.com'), |
97 check( |
79 b'certificate is for *.a.com') |
98 _verifycert(cert('*.a.com'), 'bar.foo.a.com'), b'certificate is for *.a.com' |
80 check(_verifycert(cert('*.a.com'), 'a.com'), |
99 ) |
81 b'certificate is for *.a.com') |
100 check(_verifycert(cert('*.a.com'), 'a.com'), b'certificate is for *.a.com') |
82 check(_verifycert(cert('*.a.com'), 'Xa.com'), |
101 check(_verifycert(cert('*.a.com'), 'Xa.com'), b'certificate is for *.a.com') |
83 b'certificate is for *.a.com') |
102 check(_verifycert(cert('*.a.com'), '.a.com'), b'certificate is for *.a.com') |
84 check(_verifycert(cert('*.a.com'), '.a.com'), |
|
85 b'certificate is for *.a.com') |
|
86 |
103 |
87 # only match one left-most wildcard |
104 # only match one left-most wildcard |
88 check(_verifycert(cert('f*.com'), 'foo.com'), None) |
105 check(_verifycert(cert('f*.com'), 'foo.com'), None) |
89 check(_verifycert(cert('f*.com'), 'f.com'), None) |
106 check(_verifycert(cert('f*.com'), 'f.com'), None) |
90 check(_verifycert(cert('f*.com'), 'bar.com'), |
107 check(_verifycert(cert('f*.com'), 'bar.com'), b'certificate is for f*.com') |
91 b'certificate is for f*.com') |
108 check(_verifycert(cert('f*.com'), 'foo.a.com'), b'certificate is for f*.com') |
92 check(_verifycert(cert('f*.com'), 'foo.a.com'), |
109 check(_verifycert(cert('f*.com'), 'bar.foo.com'), b'certificate is for f*.com') |
93 b'certificate is for f*.com') |
|
94 check(_verifycert(cert('f*.com'), 'bar.foo.com'), |
|
95 b'certificate is for f*.com') |
|
96 |
110 |
97 # NULL bytes are bad, CVE-2013-4073 |
111 # NULL bytes are bad, CVE-2013-4073 |
98 check(_verifycert(cert('null.python.org\x00example.org'), |
112 check( |
99 'null.python.org\x00example.org'), None) |
113 _verifycert( |
100 check(_verifycert(cert('null.python.org\x00example.org'), |
114 cert('null.python.org\x00example.org'), 'null.python.org\x00example.org' |
101 'example.org'), |
115 ), |
102 b'certificate is for null.python.org\x00example.org') |
116 None, |
103 check(_verifycert(cert('null.python.org\x00example.org'), |
117 ) |
104 'null.python.org'), |
118 check( |
105 b'certificate is for null.python.org\x00example.org') |
119 _verifycert(cert('null.python.org\x00example.org'), 'example.org'), |
|
120 b'certificate is for null.python.org\x00example.org', |
|
121 ) |
|
122 check( |
|
123 _verifycert(cert('null.python.org\x00example.org'), 'null.python.org'), |
|
124 b'certificate is for null.python.org\x00example.org', |
|
125 ) |
106 |
126 |
107 # error cases with wildcards |
127 # error cases with wildcards |
108 check(_verifycert(cert('*.*.a.com'), 'bar.foo.a.com'), |
128 check( |
109 b'certificate is for *.*.a.com') |
129 _verifycert(cert('*.*.a.com'), 'bar.foo.a.com'), |
110 check(_verifycert(cert('*.*.a.com'), 'a.com'), |
130 b'certificate is for *.*.a.com', |
111 b'certificate is for *.*.a.com') |
131 ) |
112 check(_verifycert(cert('*.*.a.com'), 'Xa.com'), |
132 check(_verifycert(cert('*.*.a.com'), 'a.com'), b'certificate is for *.*.a.com') |
113 b'certificate is for *.*.a.com') |
133 check(_verifycert(cert('*.*.a.com'), 'Xa.com'), b'certificate is for *.*.a.com') |
114 check(_verifycert(cert('*.*.a.com'), '.a.com'), |
134 check(_verifycert(cert('*.*.a.com'), '.a.com'), b'certificate is for *.*.a.com') |
115 b'certificate is for *.*.a.com') |
135 |
116 |
136 check(_verifycert(cert('a.*.com'), 'a.foo.com'), b'certificate is for a.*.com') |
117 check(_verifycert(cert('a.*.com'), 'a.foo.com'), |
137 check(_verifycert(cert('a.*.com'), 'a..com'), b'certificate is for a.*.com') |
118 b'certificate is for a.*.com') |
138 check(_verifycert(cert('a.*.com'), 'a.com'), b'certificate is for a.*.com') |
119 check(_verifycert(cert('a.*.com'), 'a..com'), |
|
120 b'certificate is for a.*.com') |
|
121 check(_verifycert(cert('a.*.com'), 'a.com'), |
|
122 b'certificate is for a.*.com') |
|
123 |
139 |
124 # wildcard doesn't match IDNA prefix 'xn--' |
140 # wildcard doesn't match IDNA prefix 'xn--' |
125 idna = u'püthon.python.org'.encode('idna').decode('ascii') |
141 idna = u'püthon.python.org'.encode('idna').decode('ascii') |
126 check(_verifycert(cert(idna), idna), None) |
142 check(_verifycert(cert(idna), idna), None) |
127 check(_verifycert(cert('x*.python.org'), idna), |
143 check( |
128 b'certificate is for x*.python.org') |
144 _verifycert(cert('x*.python.org'), idna), |
129 check(_verifycert(cert('xn--p*.python.org'), idna), |
145 b'certificate is for x*.python.org', |
130 b'certificate is for xn--p*.python.org') |
146 ) |
|
147 check( |
|
148 _verifycert(cert('xn--p*.python.org'), idna), |
|
149 b'certificate is for xn--p*.python.org', |
|
150 ) |
131 |
151 |
132 # wildcard in first fragment and IDNA A-labels in sequent fragments |
152 # wildcard in first fragment and IDNA A-labels in sequent fragments |
133 # are supported. |
153 # are supported. |
134 idna = u'www*.pythön.org'.encode('idna').decode('ascii') |
154 idna = u'www*.pythön.org'.encode('idna').decode('ascii') |
135 check(_verifycert(cert(idna), |
155 check( |
136 u'www.pythön.org'.encode('idna').decode('ascii')), |
156 _verifycert(cert(idna), u'www.pythön.org'.encode('idna').decode('ascii')), |
137 None) |
157 None, |
138 check(_verifycert(cert(idna), |
158 ) |
139 u'www1.pythön.org'.encode('idna').decode('ascii')), |
159 check( |
140 None) |
160 _verifycert(cert(idna), u'www1.pythön.org'.encode('idna').decode('ascii')), |
141 check(_verifycert(cert(idna), |
161 None, |
142 u'ftp.pythön.org'.encode('idna').decode('ascii')), |
162 ) |
143 b'certificate is for www*.xn--pythn-mua.org') |
163 check( |
144 check(_verifycert(cert(idna), |
164 _verifycert(cert(idna), u'ftp.pythön.org'.encode('idna').decode('ascii')), |
145 u'pythön.org'.encode('idna').decode('ascii')), |
165 b'certificate is for www*.xn--pythn-mua.org', |
146 b'certificate is for www*.xn--pythn-mua.org') |
166 ) |
|
167 check( |
|
168 _verifycert(cert(idna), u'pythön.org'.encode('idna').decode('ascii')), |
|
169 b'certificate is for www*.xn--pythn-mua.org', |
|
170 ) |
147 |
171 |
148 c = { |
172 c = { |
149 'notAfter': 'Jun 26 21:41:46 2011 GMT', |
173 'notAfter': 'Jun 26 21:41:46 2011 GMT', |
150 'subject': (((u'commonName', u'linuxfrz.org'),),), |
174 'subject': (((u'commonName', u'linuxfrz.org'),),), |
151 'subjectAltName': ( |
175 'subjectAltName': ( |
152 ('DNS', 'linuxfr.org'), |
176 ('DNS', 'linuxfr.org'), |
153 ('DNS', 'linuxfr.com'), |
177 ('DNS', 'linuxfr.com'), |
154 ('othername', '<unsupported>'), |
178 ('othername', '<unsupported>'), |
155 ) |
179 ), |
156 } |
180 } |
157 check(_verifycert(c, 'linuxfr.org'), None) |
181 check(_verifycert(c, 'linuxfr.org'), None) |
158 check(_verifycert(c, 'linuxfr.com'), None) |
182 check(_verifycert(c, 'linuxfr.com'), None) |
159 # Not a "DNS" entry |
183 # Not a "DNS" entry |
160 check(_verifycert(c, '<unsupported>'), |
184 check( |
161 b'certificate is for linuxfr.org, linuxfr.com') |
185 _verifycert(c, '<unsupported>'), |
|
186 b'certificate is for linuxfr.org, linuxfr.com', |
|
187 ) |
162 # When there is a subjectAltName, commonName isn't used |
188 # When there is a subjectAltName, commonName isn't used |
163 check(_verifycert(c, 'linuxfrz.org'), |
189 check( |
164 b'certificate is for linuxfr.org, linuxfr.com') |
190 _verifycert(c, 'linuxfrz.org'), |
|
191 b'certificate is for linuxfr.org, linuxfr.com', |
|
192 ) |
165 |
193 |
166 # A pristine real-world example |
194 # A pristine real-world example |
167 c = { |
195 c = { |
168 'notAfter': 'Dec 18 23:59:59 2011 GMT', |
196 'notAfter': 'Dec 18 23:59:59 2011 GMT', |
169 'subject': ( |
197 'subject': ( |