xref: /aosp_15_r20/external/cronet/net/data/gencerts/openssl_conf.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env python
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
6"""This file contains helpers for representing, manipulating, and writing
7OpenSSL configuration files [1]
8
9Configuration files are simply a collection of name=value "properties", which
10are grouped into "sections".
11
12[1] https://www.openssl.org/docs/manmaster/apps/config.html
13"""
14
15class Property(object):
16  """Represents a key/value pair in OpenSSL .cnf files.
17
18  Names and values are not quoted in any way, so callers need to pass the text
19  exactly as it should be written to the file (leading and trailing whitespace
20  doesn't matter).
21
22  For instance:
23      baseConstraints = critical, CA:false
24
25  Could be represented by a Property where:
26    name = 'baseConstraints'
27    value = 'critical, CA:false'
28  """
29  def __init__(self, name, value):
30    self.name = name
31    self.value = value
32
33
34  def write_to(self, out):
35    """Outputs this property to .cnf file."""
36    out.write('%s = %s\n' % (self.name, self.value))
37
38
39class Section(object):
40  """Represents a section in OpenSSL. For instance:
41      [CA_root]
42      preserve = true
43
44  Could be represented by a Section where:
45    name = 'CA_root'
46    properties = [Property('preserve', 'true')]
47  """
48  def __init__(self, name):
49    self.name = name
50    self.properties = []
51
52
53  def ensure_property_name_not_duplicated(self, name):
54    """Raises an exception of there is more than 1 property named |name|."""
55    count = 0
56    for prop in self.properties:
57      if prop.name == name:
58        count += 1
59    if count > 1:
60      raise Exception('Duplicate property: %s' % (name))
61
62
63  def set_property(self, name, value):
64    """Replaces, adds, or removes a Property from the Section:
65
66      - If |value| is None, then this is equivalent to calling
67        remove_property(name)
68      - If there is an existing property matching |name| then its value is
69        replaced with |value|
70      - If there are no properties matching |name| then a new one is added at
71        the end of the section
72
73    It is expected that there is AT MOST 1 property with the given name. If
74    that is not the case then this function will raise an error."""
75
76    if value is None:
77      self.remove_property(name)
78      return
79
80    self.ensure_property_name_not_duplicated(name)
81
82    for prop in self.properties:
83      if prop.name == name:
84        prop.value = value
85        return
86
87    self.add_property(name, value)
88
89
90  def add_property(self, name, value):
91    """Adds a property (allows duplicates)"""
92    self.properties.append(Property(name, value))
93
94
95  def remove_property(self, name):
96    """Removes the property with the indicated name, if it exists.
97
98    It is expected that there is AT MOST 1 property with the given name. If
99    that is not the case then this function will raise an error."""
100    self.ensure_property_name_not_duplicated(name)
101
102    for i in range(len(self.properties)):
103      if self.properties[i].name == name:
104        self.properties.pop(i)
105        return
106
107
108  def clear_properties(self):
109    """Removes all configured properties."""
110    self.properties = []
111
112
113  def write_to(self, out):
114    """Outputs the section in the format used by .cnf files"""
115    out.write('[%s]\n' % (self.name))
116    for prop in self.properties:
117      prop.write_to(out)
118    out.write('\n')
119
120
121class Config(object):
122  """Represents a .cnf (configuration) file in OpenSSL"""
123  def __init__(self):
124    self.sections = []
125
126
127  def get_section(self, name):
128    """Gets or creates a section with the given name."""
129    for section in self.sections:
130      if section.name == name:
131        return section
132    new_section = Section(name)
133    self.sections.append(new_section)
134    return new_section
135
136
137  def write_to_file(self, path):
138    """Outputs the Config to a .cnf files."""
139    with open(path, 'w') as out:
140      for section in self.sections:
141        section.write_to(out)
142