cloudFPGA (cF) API  1.0
The documentation of the source code of cloudFPGA (cF)
create_sig.py
Go to the documentation of this file.
1 # /*******************************************************************************
2 # * Copyright 2016 -- 2022 IBM Corporation
3 # *
4 # * Licensed under the Apache License, Version 2.0 (the "License");
5 # * you may not use this file except in compliance with the License.
6 # * You may obtain a copy of the License at
7 # *
8 # * http://www.apache.org/licenses/LICENSE-2.0
9 # *
10 # * Unless required by applicable law or agreed to in writing, software
11 # * distributed under the License is distributed on an "AS IS" BASIS,
12 # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # * See the License for the specific language governing permissions and
14 # * limitations under the License.
15 # *******************************************************************************/
16 
17 # *
18 # * cloudFPGA
19 # * =============================================
20 # * Created: Nov 2021
21 # * Authors: FAB, WEI, NGL, DID
22 # *
23 # * Description:
24 # * Python script to create build signature file
25 # *
26 # * ATTENTION: DO NOT MODIFY! (not even a single comma!)
27 # * This file is here to protect YOU from damaging FPGAs
28 # * during partial reconfiguration. Any change could cause
29 # * hardware damage, potentially. Thanks ;).
30 # *
31 
32 import sys
33 import os
34 import json
35 import hashlib
36 
37 # 'hardcoded' version strings
38 __THIS_FILE_VERSION_NUMBER__ = 3
39 __THIS_FILE_VERSION_STRING__ = "0.1.5"
40 __THIS_FILE_ALGORITHM_VERSION = 'hc1' # hash concat version 1
41 
42 __cfp_json_path__ = '/../cFp.json'
43 __shell_type_key__ = 'cFpSRAtype'
44 __mod_type_key__ = 'cFpMOD'
45 __dcps_folder_name__ = '/dcps/'
46 __sig_file_ending__ = 'sig'
47 __rpt_file_ending__ = 'rpt'
48 __ignore_key__ = 'ignore'
49 __ignore_hash__ = '719a965d6d8936f09550efb75bcf4bff9f956143d9f78e30b62b966b6a9ebc35' # echo -n 'ignore verify' | sha256sum
50 
51 
52 def get_file_hash(file_path):
53  # check right version
54  assert __THIS_FILE_ALGORITHM_VERSION == 'hc1'
55  sha256_hash = hashlib.sha256()
56  with open(file_path, 'rb') as f:
57  # Read and update hash string value in blocks of 4K
58  for byte_block in iter(lambda: f.read(4096), b""):
59  sha256_hash.update(byte_block)
60  return sha256_hash.hexdigest()
61 
62 
63 def get_string_hash(inp_string):
64  # check right version
65  assert __THIS_FILE_ALGORITHM_VERSION == 'hc1'
66  sha256_hash = hashlib.sha256(inp_string.encode('utf-8'))
67  return sha256_hash.hexdigest()
68 
69 
70 def get_sig_string(dcp_hash, my_hash, current_cl_cert, new_pr_hash, rpt_hash, debugging_flow=None):
71  # check right version
72  assert __THIS_FILE_ALGORITHM_VERSION == 'hc1'
73  new_cert_string = str(dcp_hash) + str(my_hash) + str(current_cl_cert) + str(new_pr_hash) + str(rpt_hash)
74  cert = hashlib.sha384(new_cert_string.encode('utf-8')).hexdigest()
75  if debugging_flow is not None:
76  print("\tnew_cert_string: {}".format(new_cert_string))
77  return cert
78 
79 
80 def main(new_bin_file_name, pr_verify_rpt_file_name):
81  # we print only on error
82  me_abs_dir = os.path.dirname(os.path.realpath(__file__))
83  me_abs_file = os.path.abspath(os.path.realpath(__file__))
84  cfp_json_file = me_abs_dir + __cfp_json_path__
85  debugging_flow = os.environ.get('CFP_DEBUGGING')
86  if debugging_flow is not None:
87  cfp_json_file = me_abs_dir + debugging_flow + '/cFp.json'
88  with open(cfp_json_file, 'r') as json_file:
89  cFp_data = json.load(json_file)
90 
91  # 1. check folders and file names
92  root_abs = os.path.realpath(me_abs_dir+"/../")
93  if debugging_flow is not None:
94  root_abs = os.path.realpath(me_abs_dir + debugging_flow + "/env/" + "/../")
95  cFp_data['abs_path'] = root_abs
96  dcps_folder = root_abs + __dcps_folder_name__
97  # folder should exist...
98  dcp_file_name = "3_top{}_STATIC.dcp".format(cFp_data[__mod_type_key__])
99  target_file_name = os.path.abspath(dcps_folder + "/" + dcp_file_name)
100  meta_file_name = "3_top{}_STATIC.json".format(cFp_data[__mod_type_key__])
101  target_meta_name = os.path.abspath(dcps_folder + "/" + meta_file_name)
102  # check preconditions and stop if necessary
103  if not os.path.isfile(target_file_name) or not os.path.isfile(target_meta_name):
104  print("[cFBuild] WARNING: {} or {} does not exist, so no signature can be created. Stop.".format(dcp_file_name, meta_file_name))
105  # return so that the tcl can continue
106  return 0
107 
108  new_bin_file_path = os.path.abspath(dcps_folder + '/' + new_bin_file_name)
109  if not os.path.isfile(new_bin_file_path):
110  print("[cFBuild] ERROR: {} is not a file. STOP.".format(new_bin_file_path))
111  exit(1)
112  ignore_pr_verify = False
113  if pr_verify_rpt_file_name == __ignore_key__:
114  ignore_pr_verify = True
115  if not ignore_pr_verify:
116  pr_verify_rpt_file_path = os.path.abspath(dcps_folder + '/' + pr_verify_rpt_file_name)
117  if not os.path.isfile(pr_verify_rpt_file_path):
118  print("[cFBuild] ERROR: {} is not a file. STOP.".format(pr_verify_rpt_file_path))
119  exit(1)
120  rpt_file_lines = []
121  with open(pr_verify_rpt_file_path) as rpt_in:
122  for line in rpt_in:
123  rpt_file_lines.append(line.rstrip())
124  pr_verify_str = ''.join(rpt_file_lines)
125 
126  sig_file_path = os.path.abspath(new_bin_file_path + '.' + __sig_file_ending__)
127  new_sig = {'build_id': __THIS_FILE_VERSION_NUMBER__, 'algorithm': __THIS_FILE_ALGORITHM_VERSION,
128  'file': new_bin_file_name}
129 
130  with open(target_meta_name, 'r') as meta_file:
131  cur_meta = json.load(meta_file)
132  current_cl_cert = cur_meta['cert']
133  pl_id = cur_meta['id']
134  # for Mantles
135  if 'pl_id' in cur_meta:
136  pl_id = cur_meta['pl_id']
137  new_sig['pl_id'] = pl_id
138 
139  # crete new cert
140  dcp_hash = get_file_hash(target_file_name)
141  my_hash = get_file_hash(me_abs_file)
142  new_pr_hash = get_file_hash(new_bin_file_path)
143  if not ignore_pr_verify:
144  # rpt_hash = get_file_hash(pr_verify_rpt_file_path) # not file!
145  rpt_hash = get_string_hash(pr_verify_str)
146  else:
147  rpt_hash = __ignore_hash__
148 
149  if debugging_flow is not None:
150  print("\tdcp hash: {}".format(dcp_hash))
151  print("\trpt hash: {}".format(rpt_hash))
152  print("\tmy hash: {}".format(my_hash))
153  print("\tsig_file_path: {}".format(sig_file_path))
154 
155  new_sig['sig'] = get_sig_string(dcp_hash, my_hash, current_cl_cert, new_pr_hash, rpt_hash,
156  debugging_flow=debugging_flow)
157  new_sig['hash'] = new_pr_hash # to check for transport errors first?
158 
159  if not ignore_pr_verify:
160  rpt_sum_line = rpt_file_lines[-1]
161  new_sig['verify_rpt'] = rpt_sum_line
162  if dcp_file_name in rpt_sum_line:
163  new_sig['verify'] = 'OK'
164  else:
165  new_sig['verify'] = 'NOK'
166  else:
167  new_sig['verify_rpt'] = __ignore_key__
168  new_sig['verify'] = 'OK'
169 
170  with open(sig_file_path, 'w') as outfile:
171  json.dump(new_sig, outfile)
172 
173  # save current pr_verify
174  if not ignore_pr_verify:
175  verify_report_name = os.path.abspath(dcps_folder + '/5_' + new_bin_file_name[2:-4] + '.' + __rpt_file_ending__)
176  os.system("cp -f {} {}".format(pr_verify_rpt_file_path, verify_report_name))
177 
178  return 0
179 
180 
181 if __name__ == '__main__':
182  # we print only on error
183  if len(sys.argv) != 3:
184  print('ERROR: Usage is {} <new-bin-file-name> <pr-verify-rpt-file-name>. STOP'.format(sys.argv[0]))
185  exit(1)
186  main(sys.argv[1], sys.argv[2])
187  exit(0)
188 
def get_string_hash(inp_string)
Definition: create_sig.py:63
def get_file_hash(file_path)
Definition: create_sig.py:52
def main(new_bin_file_name, pr_verify_rpt_file_name)
Definition: create_sig.py:80
def get_sig_string(dcp_hash, my_hash, current_cl_cert, new_pr_hash, rpt_hash, debugging_flow=None)
Definition: create_sig.py:70