• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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()
165