OLD | NEW |
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 """This script is called without any arguments to re-format all of the *.pem | 6 """This script is called without any arguments to re-format all of the *.pem |
7 files in the script's parent directory. | 7 files in the script's parent directory. |
8 | 8 |
9 The main formatting change is to run "openssl asn1parse" for each of the PEM | 9 The main formatting change is to run "openssl asn1parse" for each of the PEM |
10 block sections (except for DATA), and add that output to the comment. | 10 block sections (except for DATA), and add that output to the comment. |
11 | 11 |
12 Refer to the README file for more information. | 12 Refer to the README file for more information. |
13 """ | 13 """ |
14 | 14 |
15 import glob | 15 import glob |
16 import os | 16 import os |
17 import re | 17 import re |
18 import base64 | 18 import base64 |
19 import subprocess | 19 import subprocess |
20 | 20 |
21 | 21 |
22 def Transform(file_data): | 22 def Transform(file_data): |
23 """Returns a transformed (formatted) version of file_data""" | 23 """Returns a transformed (formatted) version of file_data""" |
24 | 24 |
25 result = '' | 25 result = '' |
26 | 26 |
27 # Get the file's description (all the text before the first PEM block) | |
28 file_description = GetTextUntilNextPemBlock(file_data) | |
29 | |
30 result += file_description + '\n' | |
31 | |
32 for block in GetPemBlocks(file_data): | 27 for block in GetPemBlocks(file_data): |
33 result += '\n\n\n' | 28 if len(result) != 0: |
34 | 29 result += '\n' |
35 result += MakePemBlockString(block.name, block.data) | |
36 | 30 |
37 # If there was a user comment (non-script-generated comment) associated | 31 # If there was a user comment (non-script-generated comment) associated |
38 # with the block, output it immediately after the block. | 32 # with the block, output it immediately before the block. |
39 user_comment = GetUserComment(block.comment) | 33 user_comment = GetUserComment(block.comment) |
40 if user_comment: | 34 if user_comment: |
41 result += '\n' + user_comment + '\n' | 35 result += user_comment |
42 | 36 |
43 # For every block except for DATA, try to pretty print the parsed ASN.1. | 37 # For every block except for DATA, try to pretty print the parsed ASN.1. |
44 # DATA blocks likely would be DER in practice, but for the purposes of | 38 # DATA blocks likely would be DER in practice, but for the purposes of |
45 # these tests seeing its structure doesn't clarify | 39 # these tests seeing its structure doesn't clarify |
46 # anything and is just a distraction. | 40 # anything and is just a distraction. |
47 if block.name != 'DATA': | 41 if block.name != 'DATA': |
48 generated_comment = GenerateCommentForBlock(block.name, block.data) | 42 generated_comment = GenerateCommentForBlock(block.name, block.data) |
49 result += '\n' + generated_comment + '\n' | 43 result += generated_comment + '\n' |
| 44 |
| 45 |
| 46 result += MakePemBlockString(block.name, block.data) |
50 | 47 |
51 return result | 48 return result |
52 | 49 |
53 | 50 |
54 def GenerateCommentForBlock(block_name, block_data): | 51 def GenerateCommentForBlock(block_name, block_data): |
55 """Returns a string describing the ASN.1 structure of block_data""" | 52 """Returns a string describing the ASN.1 structure of block_data""" |
56 | 53 |
57 p = subprocess.Popen(['openssl', 'asn1parse', '-i', '-inform', 'DER'], | 54 p = subprocess.Popen(['openssl', 'asn1parse', '-i', '-inform', 'DER'], |
58 stdout=subprocess.PIPE, stdin=subprocess.PIPE, | 55 stdout=subprocess.PIPE, stdin=subprocess.PIPE, |
59 stderr=subprocess.PIPE) | 56 stderr=subprocess.PIPE) |
60 stdout_data, stderr_data = p.communicate(input=block_data) | 57 stdout_data, stderr_data = p.communicate(input=block_data) |
61 generated_comment = '$ openssl asn1parse -i < [%s]\n%s' % (block_name, | 58 generated_comment = '$ openssl asn1parse -i < [%s]\n%s' % (block_name, |
62 stdout_data) | 59 stdout_data) |
63 return generated_comment.strip('\n') | 60 return generated_comment.strip('\n') |
64 | 61 |
65 | 62 |
66 def GetTextUntilNextPemBlock(text): | |
67 return text.split('-----BEGIN ', 1)[0].strip('\n') | |
68 | |
69 | 63 |
70 def GetUserComment(comment): | 64 def GetUserComment(comment): |
71 """Removes any script-generated lines (everything after the $ openssl line)""" | 65 """Removes any script-generated lines (everything after the $ openssl line)""" |
72 | 66 |
73 # Consider everything after "$ openssl" to be a generated comment. | 67 # Consider everything after "$ openssl" to be a generated comment. |
74 comment = comment.split('$ openssl asn1parse -i', 1)[0].strip('\n') | 68 comment = comment.split('$ openssl asn1parse -i', 1)[0] |
75 if IsEntirelyWhiteSpace(comment): | 69 if IsEntirelyWhiteSpace(comment): |
76 comment = '' | 70 comment = '' |
77 return comment | 71 return comment |
78 | 72 |
79 | 73 |
80 def MakePemBlockString(name, data): | 74 def MakePemBlockString(name, data): |
81 return ('-----BEGIN %s-----\n' | 75 return ('-----BEGIN %s-----\n' |
82 '%s' | 76 '%s' |
83 '-----END %s-----\n') % (name, EncodeDataForPem(data), name) | 77 '-----END %s-----\n') % (name, EncodeDataForPem(data), name) |
84 | 78 |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 | 120 |
127 | 121 |
128 def DecodePemBlockData(text): | 122 def DecodePemBlockData(text): |
129 text = StripAllWhitespace(text) | 123 text = StripAllWhitespace(text) |
130 return base64.b64decode(text) | 124 return base64.b64decode(text) |
131 | 125 |
132 | 126 |
133 def GetPemBlocks(data): | 127 def GetPemBlocks(data): |
134 """Returns an iterable of PemBlock""" | 128 """Returns an iterable of PemBlock""" |
135 | 129 |
| 130 comment_start = 0 |
| 131 |
136 regex = re.compile(r'-----BEGIN ([\w ]+)-----(.*?)-----END \1-----', | 132 regex = re.compile(r'-----BEGIN ([\w ]+)-----(.*?)-----END \1-----', |
137 re.DOTALL) | 133 re.DOTALL) |
138 | 134 |
139 for match in regex.finditer(data): | 135 for match in regex.finditer(data): |
140 block = PemBlock() | 136 block = PemBlock() |
141 | 137 |
142 block.name = match.group(1) | 138 block.name = match.group(1) |
143 block.data = DecodePemBlockData(match.group(2)) | 139 block.data = DecodePemBlockData(match.group(2)) |
144 | 140 |
145 # Keep track of any non-PEM text between blocks | 141 # Keep track of any non-PEM text above blocks |
146 block.comment = GetTextUntilNextPemBlock(data[match.end():]) | 142 block.comment = data[comment_start : match.start()].strip() |
| 143 comment_start = match.end() |
147 | 144 |
148 yield block | 145 yield block |
149 | 146 |
150 | 147 |
151 def WriteStringToFile(data, path): | 148 def WriteStringToFile(data, path): |
152 with open(path, "w") as f: | 149 with open(path, "w") as f: |
153 f.write(data) | 150 f.write(data) |
154 | 151 |
155 | 152 |
156 def main(): | 153 def main(): |
157 for path in GetPemFilePaths(): | 154 for path in GetPemFilePaths(): |
158 print "Processing %s ..." % (path) | 155 print "Processing %s ..." % (path) |
159 original_data = ReadFileToString(path) | 156 original_data = ReadFileToString(path) |
160 transformed_data = Transform(original_data) | 157 transformed_data = Transform(original_data) |
161 if original_data != transformed_data: | 158 if original_data != transformed_data: |
162 WriteStringToFile(transformed_data, path) | 159 WriteStringToFile(transformed_data, path) |
163 print "Rewrote %s" % (path) | 160 print "Rewrote %s" % (path) |
164 | 161 |
165 | 162 |
166 if __name__ == "__main__": | 163 if __name__ == "__main__": |
167 main() | 164 main() |
OLD | NEW |