blob: 9e6b478b67dc15bec3e1cdc37c74a162d8332801 [file] [log] [blame]
Bob Beckbc97b7a2023-04-18 08:35:15 -06001#!/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 script is called without any arguments to re-format all of the *.pem
7files in the script's parent directory.
8
9The main formatting change is to run "openssl asn1parse" for each of the PEM
10block sections (except for DATA), and add that output to the comment.
11
12Refer to the README file for more information.
13"""
14
15import glob
16import os
17import re
18import base64
19import subprocess
20
21
22def Transform(file_data):
23 """Returns a transformed (formatted) version of file_data"""
24
25 result = ''
26
27 for block in GetPemBlocks(file_data):
28 if len(result) != 0:
29 result += '\n'
30
31 # If there was a user comment (non-script-generated comment) associated
32 # with the block, output it immediately before the block.
33 user_comment = GetUserComment(block.comment)
34 if user_comment:
35 result += user_comment
36
37 # For every block except for DATA, try to pretty print the parsed ASN.1.
38 # DATA blocks likely would be DER in practice, but for the purposes of
39 # these tests seeing its structure doesn't clarify
40 # anything and is just a distraction.
41 if block.name != 'DATA':
42 generated_comment = GenerateCommentForBlock(block.name, block.data)
43 result += generated_comment + '\n'
44
45
46 result += MakePemBlockString(block.name, block.data)
47
48 return result
49
50
51def GenerateCommentForBlock(block_name, block_data):
52 """Returns a string describing the ASN.1 structure of block_data"""
53
54 p = subprocess.Popen(['openssl', 'asn1parse', '-i', '-inform', 'DER'],
55 stdout=subprocess.PIPE, stdin=subprocess.PIPE,
56 stderr=subprocess.PIPE)
57 stdout_data, stderr_data = p.communicate(input=block_data)
58 generated_comment = '$ openssl asn1parse -i < [%s]\n%s' % (block_name,
59 stdout_data)
60 return generated_comment.strip('\n')
61
62
63
64def GetUserComment(comment):
65 """Removes any script-generated lines (everything after the $ openssl line)"""
66
67 # Consider everything after "$ openssl" to be a generated comment.
68 comment = comment.split('$ openssl asn1parse -i', 1)[0]
69 if IsEntirelyWhiteSpace(comment):
70 comment = ''
71 return comment
72
73
74def MakePemBlockString(name, data):
75 return ('-----BEGIN %s-----\n'
76 '%s'
77 '-----END %s-----\n') % (name, EncodeDataForPem(data), name)
78
79
80def GetPemFilePaths():
81 """Returns an iterable for all the paths to the PEM test files"""
82
83 base_dir = os.path.dirname(os.path.realpath(__file__))
84 return glob.iglob(os.path.join(base_dir, '*.pem'))
85
86
87def ReadFileToString(path):
88 with open(path, 'r') as f:
89 return f.read()
90
91
92def WrapTextToLineWidth(text, column_width):
93 result = ''
94 pos = 0
95 while pos < len(text):
96 result += text[pos : pos + column_width] + '\n'
97 pos += column_width
98 return result
99
100
101def EncodeDataForPem(data):
102 result = base64.b64encode(data)
103 return WrapTextToLineWidth(result, 75)
104
105
106class PemBlock(object):
107 def __init__(self):
108 self.name = None
109 self.data = None
110 self.comment = None
111
112
113def StripAllWhitespace(text):
114 pattern = re.compile(r'\s+')
115 return re.sub(pattern, '', text)
116
117
118def IsEntirelyWhiteSpace(text):
119 return len(StripAllWhitespace(text)) == 0
120
121
122def DecodePemBlockData(text):
123 text = StripAllWhitespace(text)
124 return base64.b64decode(text)
125
126
127def GetPemBlocks(data):
128 """Returns an iterable of PemBlock"""
129
130 comment_start = 0
131
132 regex = re.compile(r'-----BEGIN ([\w ]+)-----(.*?)-----END \1-----',
133 re.DOTALL)
134
135 for match in regex.finditer(data):
136 block = PemBlock()
137
138 block.name = match.group(1)
139 block.data = DecodePemBlockData(match.group(2))
140
141 # Keep track of any non-PEM text above blocks
142 block.comment = data[comment_start : match.start()].strip()
143 comment_start = match.end()
144
145 yield block
146
147
148def WriteStringToFile(data, path):
149 with open(path, "w") as f:
150 f.write(data)
151
152
153def main():
154 for path in GetPemFilePaths():
155 print "Processing %s ..." % (path)
156 original_data = ReadFileToString(path)
157 transformed_data = Transform(original_data)
158 if original_data != transformed_data:
159 WriteStringToFile(transformed_data, path)
160 print "Rewrote %s" % (path)
161
162
163if __name__ == "__main__":
164 main()