Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 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 | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 """This script is called without any arguments to re-format all of the *.pem | |
| 7 files in the script's parent directory. | |
| 8 | |
| 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. | |
| 11 | |
| 12 Refer to the README file for more information. | |
| 13 """ | |
| 14 | |
| 15 import glob | |
| 16 import os | |
| 17 import re | |
| 18 import base64 | |
| 19 import subprocess | |
| 20 | |
| 21 | |
| 22 def Transform(file_data): | |
| 23 """Returns a transformed (formatted) version of file_data""" | |
| 24 | |
| 25 result = '' | |
| 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): | |
| 33 result += '\n\n\n' | |
| 34 | |
| 35 result += MakePemBlockString(block.name, block.data) | |
| 36 | |
| 37 # If there was a user comment (non-script-generated comment) associated | |
| 38 # with the block, output it immediately after the block. | |
| 39 user_comment = GetUserComment(block.comment) | |
| 40 if len(user_comment) != 0: | |
|
mattm
2015/07/28 00:51:15
"if user_comment:"
eroman
2015/07/28 01:41:23
Done.
| |
| 41 result += '\n' + user_comment + '\n' | |
| 42 | |
| 43 # 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 | |
| 45 # these tests seeing its structure doesn't clarify | |
| 46 # anything and is just a distraction. | |
| 47 if block.name != 'DATA': | |
| 48 generated_comment = GenerateCommentForBlock(block.name, block.data) | |
| 49 result += '\n' + generated_comment + '\n' | |
|
mattm
2015/07/28 00:51:15
So this is putting the text formatted version of t
eroman
2015/07/28 01:41:24
Correct. Examples at https://codereview.chromium.o
mattm
2015/07/28 04:22:14
I guess it's not a big deal either way. Having it
| |
| 50 | |
| 51 return result | |
| 52 | |
| 53 | |
| 54 def GenerateCommentForBlock(block_name, block_data): | |
| 55 """Returns a string describing the ASN.1 structure of block_data""" | |
| 56 | |
| 57 p = subprocess.Popen(['openssl', 'asn1parse', '-i', '-inform', 'DER'], | |
| 58 stdout=subprocess.PIPE, stdin=subprocess.PIPE, | |
| 59 stderr=subprocess.PIPE) | |
| 60 stdout_data, stderr_data = p.communicate(input=block_data) | |
| 61 generated_comment = '$ openssl asn1parse -i < [%s]\n%s' % (block_name, | |
| 62 stdout_data) | |
| 63 return generated_comment.strip('\n') | |
| 64 | |
| 65 | |
| 66 def GetTextUntilNextPemBlock(text): | |
| 67 result = '' | |
| 68 end = text.find('-----BEGIN ') | |
|
mattm
2015/07/28 00:51:15
slightly more pythonic: text.split('-----BEGIN ')[
eroman
2015/07/28 01:41:23
Done.
| |
| 69 if end != -1: | |
| 70 result = text[0:end] | |
| 71 return result.strip('\n') | |
| 72 | |
| 73 | |
| 74 def GetUserComment(comment): | |
| 75 """Removes any script-generated lines (everything after the $ openssl line)""" | |
| 76 | |
| 77 # Consider everything after "$ openssl" to be a generated comment. | |
| 78 pos = comment.find('$ openssl asn1parse -i') | |
|
mattm
2015/07/28 00:51:15
same as previous comment
eroman
2015/07/28 01:41:23
Done.
| |
| 79 if pos != -1: | |
| 80 comment = comment[0:pos] | |
| 81 | |
| 82 comment = comment.strip('\n') | |
| 83 if IsEntirelyWhiteSpace(comment): | |
| 84 comment = '' | |
| 85 | |
| 86 return comment | |
| 87 | |
| 88 | |
| 89 def MakePemBlockString(name, data): | |
| 90 return ('-----BEGIN %s-----\n' | |
| 91 '%s' | |
| 92 '-----END %s-----\n') % (name, EncodeDataForPem(data), name) | |
| 93 | |
| 94 | |
| 95 def GetFilePaths(): | |
|
mattm
2015/07/28 00:51:15
GetPemFilePaths
eroman
2015/07/28 01:41:24
Done.
| |
| 96 """Returns an iterable for all the paths to the PEM test files""" | |
| 97 | |
| 98 base_dir = os.path.dirname(os.path.realpath(__file__)) | |
| 99 for filename in glob.iglob('*.pem'): | |
|
mattm
2015/07/28 00:51:15
glob includes the path in the results, so you coul
eroman
2015/07/28 01:41:24
Done, much better!
In fact I don't think my previ
| |
| 100 yield os.path.join(base_dir, filename) | |
| 101 | |
| 102 | |
| 103 def ReadFileToString(path): | |
| 104 with open (path, 'r') as f: | |
|
mattm
2015/07/28 00:51:14
no space before ()
eroman
2015/07/28 01:41:23
Done.
| |
| 105 return f.read() | |
| 106 | |
| 107 | |
| 108 def WrapTextToLineWidth(text, column_width): | |
| 109 result = '' | |
| 110 pos = 0 | |
| 111 while pos < len(text): | |
| 112 result += text[pos : pos + column_width] + '\n' | |
| 113 pos += column_width | |
| 114 return result | |
| 115 | |
| 116 | |
| 117 def EncodeDataForPem(data): | |
| 118 result = base64.b64encode(data) | |
| 119 return WrapTextToLineWidth(result, 75) | |
| 120 | |
| 121 | |
| 122 class PemBlock(object): | |
| 123 def __init__(self): | |
| 124 self.name = None | |
| 125 self.data = None | |
| 126 self.comment = None | |
| 127 | |
| 128 | |
| 129 def StripAllWhitespace(text): | |
| 130 pattern = re.compile(r'\s+') | |
| 131 return re.sub(pattern, '', text) | |
| 132 | |
| 133 | |
| 134 def IsEntirelyWhiteSpace(text): | |
| 135 return len(StripAllWhitespace(text)) == 0 | |
| 136 | |
| 137 | |
| 138 def DecodePemBlockData(text): | |
| 139 text = StripAllWhitespace(text) | |
| 140 return base64.b64decode(text) | |
| 141 | |
| 142 | |
| 143 def GetPemBlocks(data): | |
| 144 """Returns an iterable of PemBlock""" | |
| 145 | |
| 146 # Position in |data| | |
| 147 pos = 0 | |
| 148 | |
| 149 while pos < len(data): | |
| 150 block = PemBlock() | |
| 151 | |
| 152 # Find the block header | |
| 153 header_pattern = re.compile('-----BEGIN ([\w ]+)-----') | |
|
mattm
2015/07/28 00:51:15
pretty nitpicky for a little helper script, but..
eroman
2015/07/28 01:41:24
Done, thanks!
| |
| 154 m = header_pattern.search(data, pos) | |
| 155 if m is None: | |
| 156 return | |
| 157 | |
| 158 block.name = m.group(1) | |
| 159 header_start = m.start() | |
| 160 header_end = m.end() + 1 | |
| 161 | |
| 162 # Find the footer | |
| 163 footer = '-----END %s-----' % (block.name) | |
| 164 footer_start = data.find(footer, header_end) | |
| 165 if footer_start == -1: | |
| 166 raise "Missing PEM footer for %s" % (block.name) | |
| 167 footer_end = footer_start + len(footer) | |
|
mattm
2015/07/28 00:51:15
you could do something fancier and make this whole
eroman
2015/07/28 01:41:23
Thanks I will look that up.
Will probably keep it
eroman
2015/07/28 18:24:52
Done - switched to re.finditer()
| |
| 168 | |
| 169 block.data = DecodePemBlockData(data[header_end:footer_start]) | |
| 170 | |
| 171 # Keep track of any non-PEM text between blocks | |
| 172 block.comment = GetTextUntilNextPemBlock(data[footer_end:]) | |
| 173 | |
| 174 pos = footer_end | |
| 175 yield block | |
| 176 | |
| 177 | |
| 178 def WriteStringToFile(data, path): | |
| 179 with open(path, "w") as f: | |
| 180 f.write(data) | |
| 181 | |
| 182 | |
| 183 def main(): | |
| 184 for path in GetFilePaths(): | |
| 185 print "Processing %s ..." % (path) | |
| 186 original_data = ReadFileToString(path) | |
| 187 transformed_data = Transform(original_data) | |
| 188 if original_data != transformed_data: | |
| 189 WriteStringToFile(transformed_data, path) | |
| 190 print "Rewrote %s" % (path) | |
| 191 | |
| 192 | |
| 193 if __name__ == "__main__": | |
| 194 main() | |
| OLD | NEW |