• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright 2015 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import base64
7import copy
8import os
9import subprocess
10import tempfile
11
12
13class RDN:
14  def __init__(self):
15    self.attrs = []
16
17  def add_attr(self, attr_type, attr_value_type, attr_value,
18               attr_modifier=None):
19    self.attrs.append((attr_type, attr_value_type, attr_value, attr_modifier))
20    return self
21
22  def __str__(self):
23    s = ''
24    for n, attr in enumerate(self.attrs):
25      s += 'attrTypeAndValue%i=SEQUENCE:attrTypeAndValueSequence%i_%i\n' % (
26          n, id(self), n)
27
28    s += '\n'
29    for n, attr in enumerate(self.attrs):
30      attr_type, attr_value_type, attr_value, attr_modifier = attr
31      s += '[attrTypeAndValueSequence%i_%i]\n' % (id(self), n)
32      # Note the quotes around the string value here, which is necessary for
33      # trailing whitespace to be included by openssl.
34      s += 'type=OID:%s\n' % attr_type
35      s += 'value='
36      if attr_modifier:
37        s += attr_modifier + ','
38      s += '%s:"%s"\n' % (attr_value_type, attr_value)
39
40    return s
41
42
43class NameGenerator:
44  def __init__(self):
45    self.rdns = []
46
47  def token(self):
48    return b"NAME"
49
50  def add_rdn(self):
51    rdn = RDN()
52    self.rdns.append(rdn)
53    return rdn
54
55  def __str__(self):
56    s = 'asn1 = SEQUENCE:rdnSequence%i\n\n[rdnSequence%i]\n' % (
57        id(self), id(self))
58    for n, rdn in enumerate(self.rdns):
59      s += 'rdn%i = SET:rdnSet%i_%i\n' % (n, id(self), n)
60
61    s += '\n'
62
63    for n, rdn in enumerate(self.rdns):
64      s += '[rdnSet%i_%i]\n%s\n' % (id(self), n, rdn)
65
66    return s
67
68
69def generate(s, fn):
70  out_fn = os.path.join('..', 'names', fn + '.pem')
71  conf_tempfile = tempfile.NamedTemporaryFile(mode='wt', encoding='utf-8')
72  conf_tempfile.write(str(s))
73  conf_tempfile.flush()
74  der_tmpfile = tempfile.NamedTemporaryFile()
75  subprocess.check_call([
76      'openssl', 'asn1parse', '-genconf', conf_tempfile.name, '-i', '-out',
77      der_tmpfile.name
78  ],
79                        stdout=subprocess.DEVNULL)
80  conf_tempfile.close()
81
82  description_tmpfile = tempfile.NamedTemporaryFile()
83  subprocess.check_call(['der2ascii', '-i', der_tmpfile.name],
84                        stdout=description_tmpfile)
85
86  output_file = open(out_fn, 'wb')
87  description_tmpfile.seek(0)
88  output_file.write(description_tmpfile.read())
89  output_file.write(b'-----BEGIN NAME-----\n')
90  output_file.write(base64.encodebytes(der_tmpfile.read()))
91  output_file.write(b'-----END NAME-----\n')
92  output_file.close()
93
94
95def unmangled(s):
96  return s
97
98
99def extra_whitespace(s):
100  return '  ' + s.replace(' ', '   ') + '  '
101
102
103def case_swap(s):
104  return s.swapcase()
105
106
107def main():
108  for valuetype in ('PRINTABLESTRING', 'T61STRING', 'UTF8', 'BMPSTRING',
109                    'UNIVERSALSTRING'):
110    for string_mangler in (unmangled, extra_whitespace, case_swap):
111      n=NameGenerator()
112      n.add_rdn().add_attr('countryName', 'PRINTABLESTRING', 'US')
113      n.add_rdn().add_attr('stateOrProvinceName',
114                           valuetype,
115                           string_mangler('New York'))
116      n.add_rdn().add_attr('localityName',
117                           valuetype,
118                           string_mangler("ABCDEFGHIJKLMNOPQRSTUVWXYZ "
119                                          "abcdefghijklmnopqrstuvwxyz "
120                                          "0123456789 '()+,-./:=?"))
121
122      n_extra_attr = copy.deepcopy(n)
123      n_extra_attr.rdns[-1].add_attr('organizationName',
124                                     valuetype,
125                                     string_mangler('Name of company'))
126
127      n_dupe_attr = copy.deepcopy(n)
128      n_dupe_attr.rdns[-1].add_attr(*n_dupe_attr.rdns[-1].attrs[-1])
129
130      n_extra_rdn = copy.deepcopy(n)
131      n_extra_rdn.add_rdn().add_attr('organizationName',
132                                     valuetype,
133                                     string_mangler('Name of company'))
134
135      filename_base = 'ascii-' + valuetype + '-' + string_mangler.__name__
136
137      generate(n, filename_base)
138      generate(n_extra_attr, filename_base + '-extra_attr')
139      generate(n_dupe_attr, filename_base + '-dupe_attr')
140      generate(n_extra_rdn, filename_base + '-extra_rdn')
141
142  for valuetype in ('UTF8', 'BMPSTRING', 'UNIVERSALSTRING'):
143    n=NameGenerator()
144    n.add_rdn().add_attr('countryName', 'PRINTABLESTRING', 'JP')
145    n.add_rdn().add_attr('localityName', valuetype, "\u6771\u4eac",
146                         "FORMAT:UTF8")
147
148    filename_base = 'unicode_bmp-' + valuetype + '-' + 'unmangled'
149    generate(n, filename_base)
150
151  for valuetype in ('UTF8', 'UNIVERSALSTRING'):
152    n=NameGenerator()
153    n.add_rdn().add_attr('countryName', 'PRINTABLESTRING', 'JP')
154    n.add_rdn().add_attr('localityName', valuetype, "\U0001d400\U0001d419",
155                         "FORMAT:UTF8")
156
157    filename_base = 'unicode_supplementary-' + valuetype + '-' + 'unmangled'
158    generate(n, filename_base)
159
160  generate("""asn1 = SEQUENCE:rdnSequence
161[rdnSequence]
162rdn0 = SET:rdnSet0
163[rdnSet0]
164attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
165[attrTypeAndValueSequence0_0]
166type=OID:countryName
167value=PRINTABLESTRING:"US"
168extra=PRINTABLESTRING:"hello world"
169""", "invalid-AttributeTypeAndValue-extradata")
170
171  generate("""asn1 = SEQUENCE:rdnSequence
172[rdnSequence]
173rdn0 = SET:rdnSet0
174[rdnSet0]
175attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
176[attrTypeAndValueSequence0_0]
177type=OID:countryName
178""", "invalid-AttributeTypeAndValue-onlyOneElement")
179
180  generate("""asn1 = SEQUENCE:rdnSequence
181[rdnSequence]
182rdn0 = SET:rdnSet0
183[rdnSet0]
184attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
185[attrTypeAndValueSequence0_0]
186""", "invalid-AttributeTypeAndValue-empty")
187
188  generate("""asn1 = SEQUENCE:rdnSequence
189[rdnSequence]
190rdn0 = SET:rdnSet0
191[rdnSet0]
192attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
193[attrTypeAndValueSequence0_0]
194type=PRINTABLESTRING:"hello world"
195value=PRINTABLESTRING:"US"
196""", "invalid-AttributeTypeAndValue-badAttributeType")
197
198  generate("""asn1 = SEQUENCE:rdnSequence
199[rdnSequence]
200rdn0 = SET:rdnSet0
201[rdnSet0]
202attrTypeAndValue0=SET:attrTypeAndValueSequence0_0
203[attrTypeAndValueSequence0_0]
204type=OID:countryName
205value=PRINTABLESTRING:"US"
206""", "invalid-AttributeTypeAndValue-setNotSequence")
207
208  generate("""asn1 = SEQUENCE:rdnSequence
209[rdnSequence]
210rdn0 = SEQUENCE:rdnSet0
211[rdnSet0]
212attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
213[attrTypeAndValueSequence0_0]
214type=OID:countryName
215value=PRINTABLESTRING:"US"
216""", "invalid-RDN-sequenceInsteadOfSet")
217
218  generate("""asn1 = SEQUENCE:rdnSequence
219[rdnSequence]
220rdn0 = SET:rdnSet0
221[rdnSet0]
222""", "invalid-RDN-empty")
223
224  generate("""asn1 = SET:rdnSequence
225[rdnSequence]
226rdn0 = SET:rdnSet0
227[rdnSet0]
228attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
229[attrTypeAndValueSequence0_0]
230type=OID:countryName
231value=PRINTABLESTRING:"US"
232""", "invalid-Name-setInsteadOfSequence")
233
234  generate("""asn1 = SEQUENCE:rdnSequence
235[rdnSequence]
236""", "valid-Name-empty")
237
238  # Certs with a RDN that is sorted differently due to length of the values, but
239  # which should compare equal when normalized.
240  generate("""asn1 = SEQUENCE:rdnSequence
241[rdnSequence]
242rdn0 = SET:rdnSet0
243[rdnSet0]
244attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
245attrTypeAndValue1=SEQUENCE:attrTypeAndValueSequence0_1
246[attrTypeAndValueSequence0_0]
247type=OID:stateOrProvinceName
248value=PRINTABLESTRING:"    state"
249[attrTypeAndValueSequence0_1]
250type=OID:localityName
251value=PRINTABLESTRING:"locality"
252""", "ascii-PRINTABLESTRING-rdn_sorting_1")
253
254  generate("""asn1 = SEQUENCE:rdnSequence
255[rdnSequence]
256rdn0 = SET:rdnSet0
257[rdnSet0]
258attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
259attrTypeAndValue1=SEQUENCE:attrTypeAndValueSequence0_1
260[attrTypeAndValueSequence0_0]
261type=OID:stateOrProvinceName
262value=PRINTABLESTRING:"state"
263[attrTypeAndValueSequence0_1]
264type=OID:localityName
265value=PRINTABLESTRING:" locality"
266""", "ascii-PRINTABLESTRING-rdn_sorting_2")
267
268  # Certs with a RDN that is sorted differently due to length of the values, and
269  # also contains multiple values with the same type.
270  generate("""asn1 = SEQUENCE:rdnSequence
271[rdnSequence]
272rdn0 = SET:rdnSet0
273[rdnSet0]
274attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
275attrTypeAndValue1=SEQUENCE:attrTypeAndValueSequence0_1
276attrTypeAndValue2=SEQUENCE:attrTypeAndValueSequence0_2
277attrTypeAndValue3=SEQUENCE:attrTypeAndValueSequence0_3
278attrTypeAndValue4=SEQUENCE:attrTypeAndValueSequence0_4
279[attrTypeAndValueSequence0_0]
280type=OID:domainComponent
281value=IA5STRING:"     cOm"
282[attrTypeAndValueSequence0_1]
283type=OID:domainComponent
284value=IA5STRING:"eXaMple"
285[attrTypeAndValueSequence0_2]
286type=OID:domainComponent
287value=IA5STRING:"wWw"
288[attrTypeAndValueSequence0_3]
289type=OID:localityName
290value=PRINTABLESTRING:"NEw"
291[attrTypeAndValueSequence0_4]
292type=OID:localityName
293value=PRINTABLESTRING:"   yORk    "
294""", "ascii-mixed-rdn_dupetype_sorting_1")
295
296  generate("""asn1 = SEQUENCE:rdnSequence
297[rdnSequence]
298rdn0 = SET:rdnSet0
299[rdnSet0]
300attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
301attrTypeAndValue1=SEQUENCE:attrTypeAndValueSequence0_1
302attrTypeAndValue2=SEQUENCE:attrTypeAndValueSequence0_2
303attrTypeAndValue3=SEQUENCE:attrTypeAndValueSequence0_3
304attrTypeAndValue4=SEQUENCE:attrTypeAndValueSequence0_4
305[attrTypeAndValueSequence0_0]
306type=OID:domainComponent
307value=IA5STRING:"cOM"
308[attrTypeAndValueSequence0_1]
309type=OID:domainComponent
310value=IA5STRING:"eXampLE"
311[attrTypeAndValueSequence0_2]
312type=OID:domainComponent
313value=IA5STRING:"    Www  "
314[attrTypeAndValueSequence0_3]
315type=OID:localityName
316value=PRINTABLESTRING:"   nEw            "
317[attrTypeAndValueSequence0_4]
318type=OID:localityName
319value=PRINTABLESTRING:"yoRK"
320""", "ascii-mixed-rdn_dupetype_sorting_2")
321
322  # Minimal valid config. Copy and modify this one when generating new invalid
323  # configs.
324  generate("""asn1 = SEQUENCE:rdnSequence
325[rdnSequence]
326rdn0 = SET:rdnSet0
327[rdnSet0]
328attrTypeAndValue0=SEQUENCE:attrTypeAndValueSequence0_0
329[attrTypeAndValueSequence0_0]
330type=OID:countryName
331value=PRINTABLESTRING:"US"
332""", "valid-minimal")
333
334  # Single Name that exercises all of the string types, unicode (basic and
335  # supplemental planes), whitespace collapsing, case folding, as well as SET
336  # sorting.
337  n = NameGenerator()
338  rdn1 = n.add_rdn()
339  rdn1.add_attr('countryName', 'PRINTABLESTRING', 'AA')
340  rdn1.add_attr('stateOrProvinceName', 'T61STRING', '  AbCd  Ef  ')
341  rdn1.add_attr('localityName', 'UTF8', "  Ab\u6771\u4eac ", "FORMAT:UTF8")
342  rdn1.add_attr('organizationName', 'BMPSTRING', " aB  \u6771\u4eac  cD ",
343                "FORMAT:UTF8")
344  rdn1.add_attr('organizationalUnitName', 'UNIVERSALSTRING',
345                " \U0001d400  A  bC ", "FORMAT:UTF8")
346  rdn1.add_attr('domainComponent', 'IA5STRING', 'eXaMpLe')
347  rdn2 = n.add_rdn()
348  rdn2.add_attr('localityName', 'UTF8', "AAA")
349  rdn2.add_attr('localityName', 'BMPSTRING', "aaa")
350  rdn3 = n.add_rdn()
351  rdn3.add_attr('localityName', 'PRINTABLESTRING', "cCcC")
352  generate(n, "unicode-mixed-unnormalized")
353  # Expected normalized version of above.
354  n = NameGenerator()
355  rdn1 = n.add_rdn()
356  rdn1.add_attr('countryName', 'UTF8', 'aa')
357  rdn1.add_attr('stateOrProvinceName', 'T61STRING', '  AbCd  Ef  ')
358  rdn1.add_attr('localityName', 'UTF8', "ab\u6771\u4eac", "FORMAT:UTF8")
359  rdn1.add_attr('organizationName', 'UTF8', "ab \u6771\u4eac cd", "FORMAT:UTF8")
360  rdn1.add_attr('organizationalUnitName', 'UTF8', "\U0001d400 a bc",
361                "FORMAT:UTF8")
362  rdn1.add_attr('domainComponent', 'UTF8', 'example')
363  rdn2 = n.add_rdn()
364  rdn2.add_attr('localityName', 'UTF8', "aaa")
365  rdn2.add_attr('localityName', 'UTF8', "aaa")
366  rdn3 = n.add_rdn()
367  rdn3.add_attr('localityName', 'UTF8', "cccc")
368  generate(n, "unicode-mixed-normalized")
369
370
371if __name__ == '__main__':
372  main()
373