Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: net/data/verify_certificate_chain_unittest/common.py

Issue 2805213004: Refactor how net/data/verify_certificate_chain_unittest/* (Closed)
Patch Set: rebase Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2015 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Set of helpers to generate signed X.509v3 certificates. 6 """Set of helpers to generate signed X.509v3 certificates.
7 7
8 This works by shelling out calls to the 'openssl req' and 'openssl ca' 8 This works by shelling out calls to the 'openssl req' and 'openssl ca'
9 commands, and passing the appropriate command line flags and configuration file 9 commands, and passing the appropriate command line flags and configuration file
10 (.cnf). 10 (.cnf).
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
45 KEY_PURPOSE_ANY = 'anyExtendedKeyUsage' 45 KEY_PURPOSE_ANY = 'anyExtendedKeyUsage'
46 KEY_PURPOSE_SERVER_AUTH = 'serverAuth' 46 KEY_PURPOSE_SERVER_AUTH = 'serverAuth'
47 KEY_PURPOSE_CLIENT_AUTH = 'clientAuth' 47 KEY_PURPOSE_CLIENT_AUTH = 'clientAuth'
48 48
49 DEFAULT_KEY_PURPOSE = KEY_PURPOSE_SERVER_AUTH 49 DEFAULT_KEY_PURPOSE = KEY_PURPOSE_SERVER_AUTH
50 50
51 # Counters used to generate unique (but readable) path names. 51 # Counters used to generate unique (but readable) path names.
52 g_cur_path_id = {} 52 g_cur_path_id = {}
53 53
54 # Output paths used: 54 # Output paths used:
55 # - g_out_dir: where any temporary files (cert req, signing db etc) are 55 # - g_tmp_dir: where any temporary files (cert req, signing db etc) are
56 # saved to. 56 # saved to.
57 # - g_script_name: the name of the invoking script. For instance if this is 57
58 # being run by generate-foo.py then g_script_name will be 58 # See init() for how these are assigned.
59 # 'foo' 59 g_tmp_dir = None
60 #
61 # See init() for how these are assigned, based on the name of the calling
62 # script.
63 g_out_dir = None
64 g_script_name = None
65 60
66 # The default validity range of generated certificates. Can be modified with 61 # The default validity range of generated certificates. Can be modified with
67 # set_default_validity_range(). 62 # set_default_validity_range().
68 g_default_start_date = JANUARY_1_2015_UTC 63 g_default_start_date = JANUARY_1_2015_UTC
69 g_default_end_date = JANUARY_1_2016_UTC 64 g_default_end_date = JANUARY_1_2016_UTC
70 65
71 66
72 def set_default_validity_range(start_date, end_date): 67 def set_default_validity_range(start_date, end_date):
73 """Sets the validity range that will be used for certificates created with 68 """Sets the validity range that will be used for certificates created with
74 Certificate""" 69 Certificate"""
(...skipping 13 matching lines...) Expand all
88 g_cur_path_id[lowercase_name] = path_id + 1 83 g_cur_path_id[lowercase_name] = path_id + 1
89 84
90 # Use a short and clean name for the first use of this name. 85 # Use a short and clean name for the first use of this name.
91 if path_id == 0: 86 if path_id == 0:
92 return name 87 return name
93 88
94 # Otherwise append the count to make it unique. 89 # Otherwise append the count to make it unique.
95 return '%s_%d' % (name, path_id) 90 return '%s_%d' % (name, path_id)
96 91
97 92
98 def get_path_in_output_dir(name, suffix): 93 def get_path_in_tmp_dir(name, suffix):
99 return os.path.join(g_out_dir, '%s%s' % (name, suffix)) 94 return os.path.join(g_tmp_dir, '%s%s' % (name, suffix))
100 95
101 96
102 class Key(object): 97 class Key(object):
103 """Describes a public + private key pair. It is a dumb wrapper around an 98 """Describes a public + private key pair. It is a dumb wrapper around an
104 on-disk key.""" 99 on-disk key."""
105 100
106 def __init__(self, path): 101 def __init__(self, path):
107 self.path = path 102 self.path = path
108 103
109 104
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 path.""" 147 path."""
153 return get_or_generate_key(['openssl', 'ecparam', '-name', named_curve, 148 return get_or_generate_key(['openssl', 'ecparam', '-name', named_curve,
154 '-genkey'], path) 149 '-genkey'], path)
155 150
156 151
157 def create_key_path(base_name): 152 def create_key_path(base_name):
158 """Generates a name that contains |base_name| in it, and is relative to the 153 """Generates a name that contains |base_name| in it, and is relative to the
159 "keys/" directory. If create_key_path(xxx) is called more than once during 154 "keys/" directory. If create_key_path(xxx) is called more than once during
160 the script run, a suffix will be added.""" 155 the script run, a suffix will be added."""
161 156
162 # Save keys to CWD/keys/<generate-script-name>/*.key 157 #Save keys to CWD / keys / < generate - script - name >/*.key
mattm 2017/05/02 06:43:46 indentation, space after # are the internal space
eroman 2017/05/02 19:20:23 Done. (I think these lines got messed up by some
163 # Hack: if the script name was generate-certs.py, then just save to
164 # 'keys/*.key' (used by external consumers of common.py)
165 keys_dir = 'keys' 158 keys_dir = 'keys'
166 if g_script_name != 'certs':
167 keys_dir = os.path.join(keys_dir, g_script_name)
168 159
169 # Create the keys directory if it doesn't exist 160 # Create the keys directory if it doesn't exist
170 if not os.path.exists(keys_dir): 161 if not os.path.exists(keys_dir):
171 os.makedirs(keys_dir) 162 os.makedirs(keys_dir)
172 163
173 return get_unique_path_id(os.path.join(keys_dir, base_name)) + '.key' 164 return get_unique_path_id(os.path.join(keys_dir, base_name)) + '.key'
174 165
175 166
176 class Certificate(object): 167 class Certificate(object):
177 """Helper for building an X.509 certificate.""" 168 """Helper for building an X.509 certificate."""
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 self.md_flags = ['-md', md] 243 self.md_flags = ['-md', md]
253 244
254 245
255 def get_extensions(self): 246 def get_extensions(self):
256 return self.config.get_section('req_ext') 247 return self.config.get_section('req_ext')
257 248
258 249
259 def get_path(self, suffix): 250 def get_path(self, suffix):
260 """Forms a path to an output file for this certificate, containing the 251 """Forms a path to an output file for this certificate, containing the
261 indicated suffix. The certificate's name will be used as its basis.""" 252 indicated suffix. The certificate's name will be used as its basis."""
262 return os.path.join(g_out_dir, '%s%s' % (self.path_id, suffix)) 253 return os.path.join(g_tmp_dir, '%s%s' % (self.path_id, suffix))
263 254
264 255
265 def get_name_path(self, suffix): 256 def get_name_path(self, suffix):
266 """Forms a path to an output file for this CA, containing the indicated 257 """Forms a path to an output file for this CA, containing the indicated
267 suffix. If multiple certificates have the same name, they will use the same 258 suffix. If multiple certificates have the same name, they will use the same
268 path.""" 259 path."""
269 return get_path_in_output_dir(self.name, suffix) 260 return get_path_in_tmp_dir(self.name, suffix)
270 261
271 262
272 def set_key(self, key): 263 def set_key(self, key):
273 assert self.finalized is False 264 assert self.finalized is False
274 self.set_key_internal(key) 265 self.set_key_internal(key)
275 266
276 267
277 def set_key_internal(self, key): 268 def set_key_internal(self, key):
278 self.key = key 269 self.key = key
279 270
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
407 398
408 # -------------------------------------- 399 # --------------------------------------
409 # 'ca' section 400 # 'ca' section
410 # -------------------------------------- 401 # --------------------------------------
411 402
412 section = self.config.get_section('ca') 403 section = self.config.get_section('ca')
413 section.set_property('default_ca', 'root_ca') 404 section.set_property('default_ca', 'root_ca')
414 405
415 section = self.config.get_section('root_ca') 406 section = self.config.get_section('root_ca')
416 section.set_property('certificate', self.get_cert_path()) 407 section.set_property('certificate', self.get_cert_path())
417 section.set_property('new_certs_dir', g_out_dir) 408 section.set_property('new_certs_dir', g_tmp_dir)
418 section.set_property('serial', self.get_serial_path()) 409 section.set_property('serial', self.get_serial_path())
419 section.set_property('database', self.get_database_path()) 410 section.set_property('database', self.get_database_path())
420 section.set_property('unique_subject', 'no') 411 section.set_property('unique_subject', 'no')
421 412
422 # These will get overridden via command line flags. 413 # These will get overridden via command line flags.
423 section.set_property('default_days', '365') 414 section.set_property('default_days', '365')
424 section.set_property('default_md', 'sha256') 415 section.set_property('default_md', 'sha256')
425 416
426 section.set_property('policy', 'policy_anything') 417 section.set_property('policy', 'policy_anything')
427 section.set_property('email_in_dn', 'no') 418 section.set_property('email_in_dn', 'no')
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 section = self.config.get_section('crl_ext') 450 section = self.config.get_section('crl_ext')
460 section.set_property('authorityKeyIdentifier', 'keyid:always') 451 section.set_property('authorityKeyIdentifier', 'keyid:always')
461 section.set_property('authorityInfoAccess', '@issuer_info') 452 section.set_property('authorityInfoAccess', '@issuer_info')
462 453
463 454
464 def text_data_to_pem(block_header, text_data): 455 def text_data_to_pem(block_header, text_data):
465 return '%s\n-----BEGIN %s-----\n%s\n-----END %s-----\n' % (text_data, 456 return '%s\n-----BEGIN %s-----\n%s\n-----END %s-----\n' % (text_data,
466 block_header, base64.b64encode(text_data), block_header) 457 block_header, base64.b64encode(text_data), block_header)
467 458
468 459
469 class TrustAnchor(object): 460 def write_chain(description, chain, out_pem):
470 """Structure that represents a trust anchor.""" 461 """Writes the chain to a .pem file as a series of CERTIFICATE blocks"""
471
472 def __init__(self, cert, constrained=False):
473 self.cert = cert
474 self.constrained = constrained
475
476
477 def get_pem(self):
478 """Returns a PEM block string describing this trust anchor."""
479
480 cert_data = self.cert.get_cert_pem()
481 block_name = 'TRUST_ANCHOR_UNCONSTRAINED'
482 if self.constrained:
483 block_name = 'TRUST_ANCHOR_CONSTRAINED'
484
485 # Use a different block name in the .pem file, depending on the anchor type.
486 return cert_data.replace('CERTIFICATE', block_name)
487
488
489 def write_test_file(description, chain, trust_anchor, utc_time, key_purpose,
490 verify_result, errors, out_pem=None):
491 """Writes a test file that contains all the inputs necessary to run a
492 verification on a certificate chain."""
493 462
494 # Prepend the script name that generated the file to the description. 463 # Prepend the script name that generated the file to the description.
495 test_data = '[Created by: %s]\n\n%s\n' % (sys.argv[0], description) 464 test_data = '[Created by: %s]\n\n%s\n' % (sys.argv[0], description)
496 465
497 # Write the certificate chain to the output file. 466 # Write the certificate chain to the output file.
498 for cert in chain: 467 for cert in chain:
499 test_data += '\n' + cert.get_cert_pem() 468 test_data += '\n' + cert.get_cert_pem()
500 469
501 test_data += '\n' + trust_anchor.get_pem()
502 test_data += '\n' + text_data_to_pem('TIME', utc_time)
503
504 verify_result_string = 'SUCCESS' if verify_result else 'FAIL'
505 test_data += '\n' + text_data_to_pem('VERIFY_RESULT', verify_result_string)
506
507 test_data += '\n' + text_data_to_pem('KEY_PURPOSE', key_purpose)
508
509 if errors is not None:
510 test_data += '\n' + text_data_to_pem('ERRORS', errors)
511
512 if not out_pem:
513 out_pem = g_script_name + '.pem'
514 write_string_to_file(test_data, out_pem) 470 write_string_to_file(test_data, out_pem)
515 471
516 472
517 def write_string_to_file(data, path): 473 def write_string_to_file(data, path):
518 with open(path, 'w') as f: 474 with open(path, 'w') as f:
519 f.write(data) 475 f.write(data)
520 476
521 477
522 def read_file_to_string(path): 478 def read_file_to_string(path):
523 with open(path, 'r') as f: 479 with open(path, 'r') as f:
524 return f.read() 480 return f.read()
525 481
526 482
527 def init(invoking_script_path): 483 def init(invoking_script_path):
528 """Creates an output directory to contain all the temporary files that may be 484 """Creates an output directory to contain all the temporary files that may be
529 created, as well as determining the path for the final output. These paths 485 created, as well as determining the path for the final output. These paths
530 are all based off of the name of the calling script. 486 are all based off of the name of the calling script.
531 """ 487 """
532 488
533 global g_out_dir 489 global g_tmp_dir
534 global g_script_name
535 490
536 # The scripts assume to be run from within their containing directory (paths 491 # The scripts assume to be run from within their containing directory (paths
537 # to things like "keys/" are written relative). 492 # to things like "keys/" are written relative).
538 expected_cwd = os.path.realpath(os.path.dirname(invoking_script_path)) 493 expected_cwd = os.path.realpath(os.path.dirname(invoking_script_path))
539 actual_cwd = os.path.realpath(os.getcwd()) 494 actual_cwd = os.path.realpath(os.getcwd())
540 if actual_cwd != expected_cwd: 495 if actual_cwd != expected_cwd:
541 sys.stderr.write( 496 sys.stderr.write(
542 ('Your current working directory must be that containing the python ' 497 ('Your current working directory must be that containing the python '
543 'scripts:\n%s\nas the script may reference paths relative to this\n') 498 'scripts:\n%s\nas the script may reference paths relative to this\n')
544 % (expected_cwd)) 499 % (expected_cwd))
545 sys.exit(1) 500 sys.exit(1)
546 501
547 # Base the output name off of the invoking script's name.
548 out_name = os.path.splitext(os.path.basename(invoking_script_path))[0]
549
550 # Strip the leading 'generate-'
551 if out_name.startswith('generate-'):
552 out_name = out_name[9:]
553
554 # Use an output directory with the same name as the invoking script. 502 # Use an output directory with the same name as the invoking script.
555 g_out_dir = os.path.join('out', out_name) 503 g_tmp_dir = 'out'
556 504
557 # Ensure the output directory exists and is empty. 505 # Ensure the output directory exists and is empty.
558 sys.stdout.write('Creating output directory: %s\n' % (g_out_dir)) 506 sys.stdout.write('Creating output directory: %s\n' % (g_tmp_dir))
559 shutil.rmtree(g_out_dir, True) 507 shutil.rmtree(g_tmp_dir, True)
560 os.makedirs(g_out_dir) 508 os.makedirs(g_tmp_dir)
561
562 g_script_name = out_name
563 509
564 510
565 def create_self_signed_root_certificate(name): 511 def create_self_signed_root_certificate(name):
566 return Certificate(name, TYPE_CA, None) 512 return Certificate(name, TYPE_CA, None)
567 513
568 514
569 def create_intermediate_certificate(name, issuer): 515 def create_intermediate_certificate(name, issuer):
570 return Certificate(name, TYPE_CA, issuer) 516 return Certificate(name, TYPE_CA, issuer)
571 517
572 518
573 def create_end_entity_certificate(name, issuer): 519 def create_end_entity_certificate(name, issuer):
574 return Certificate(name, TYPE_END_ENTITY, issuer) 520 return Certificate(name, TYPE_END_ENTITY, issuer)
575 521
576 init(sys.argv[0]) 522 init(sys.argv[0])
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698