99# that the user is authorised to sign the firmware.
1010
1111# Please configure gpg-agent with `max-cache-ttl 0` when deploying.
12+ # Run as MBEDTLS_FW_SIGN_SERVER_IP=X.X.X.X && python firmware_signer.py
1213
13- import flask
1414import random
1515import os
1616import string
1717import shlex
1818import shutil
1919import subprocess
2020import pathlib
21+ from typing import Tuple
2122
22- from typing import Text , Tuple
23-
24-
23+ import flask
2524################ /* Configuration Parameters #################
26- app = flask .Flask (__name__ )
25+ APP = flask .Flask (__name__ )
2726
2827# Allow the host to set the IP for the server3
29- server_ip = "0.0.0.0" # Will launch the server but dl links won't work.
30- server_port = "5000" # Port to bind to.
31- cleanup_on_startup = True # Cleanup the temporary directory on launch.
28+ SERVER_IP = "0.0.0.0" # Will launch the server but dl links won't work.
29+ SERVER_PORT = "5000" # Port to bind to.
30+ CLEANUP_ON_STARTUP = True # Cleanup the temporary directory on launch.
3231
3332################ Configuration Parameters */ #################
3433
3534# ENV overrides
3635if "MBEDTLS_FW_SIGN_SERVER_IP" in os .environ :
37- server_ip = os .environ ["MBEDTLS_FW_SIGN_SERVER_IP" ]
36+ SERVER_IP = os .environ ["MBEDTLS_FW_SIGN_SERVER_IP" ]
3837
3938if "MBEDTLS_FW_SIGN_SERVER_PORT" in os .environ :
40- server_port = os .environ ["MBEDTLS_FW_SIGN_SERVER_PORT" ]
39+ SERVER_PORT = os .environ ["MBEDTLS_FW_SIGN_SERVER_PORT" ]
4140
42- download_pfix = "http://{}:{}/" .format (server_ip , server_port )
43- sign_cmd = ("gpg --detach-sign --pinentry-mode loopback"
41+ DOWNLOAD_PFIX = "http://{}:{}/" .format (SERVER_IP , SERVER_PORT )
42+ SIGN_CMD = ("gpg --detach-sign --pinentry-mode loopback"
4443 " --passphrase '{pasw}' --armor --batch {tarb}" )
45- verify_cmd = "gpg --verify {sig} {tarb}"
46- sum_cmd = "sha256sum {tarb}"
47- zip_cmd = "zip -r {name}.zip ."
44+ VERIFY_CMD = "gpg --verify {sig} {tarb}"
45+ SUM_CMD = "sha256sum {tarb}"
46+ ZIP_CMD = "zip -r {name}.zip ."
4847
4948
5049def do_shell_exec (exec_string : str ) -> Tuple [int , str , str ]:
@@ -72,19 +71,22 @@ def randomise_path(name: str) -> str:
7271 return os .path .join ("tmp" , "{}_{}" .format (name , pfix ))
7372
7473
75- @app .route ('/' )
74+ @APP .route ('/' )
7675def main () -> flask .typing .ResponseReturnValue :
76+ """Invoked on main landing page."""
7777 return flask .render_template ("index.html" )
7878
7979
80- @app .route ('/<path>/<filename>' )
80+ @APP .route ('/<path>/<filename>' )
8181def download (path : str , filename : str ) -> flask .typing .ResponseReturnValue :
82+ """Invoked on {SERVERIP:PORT}/tmp_workdir/filename api endpoint."""
8283 path = os .path .join ("tmp" , path )
8384 return flask .send_from_directory (path , filename , as_attachment = True )
8485
85-
86- @app .route ('/sign' , methods = ['POST' ])
86+ # pylint: disable=too-many-locals, inconsistent-return-statements
87+ @APP .route ('/sign' , methods = ['POST' ])
8788def sign () -> flask .typing .ResponseReturnValue :
89+ """Invoked on {SERVERIP:PORT}/sign api endpoint."""
8890 if flask .request .method == 'POST' :
8991
9092 # Accept the file
@@ -100,8 +102,8 @@ def sign() -> flask.typing.ResponseReturnValue:
100102 sign_fname = artf_name + ".asc"
101103 tmp_workdir = randomise_path (artf_basename )
102104
103- if cleanup_on_startup :
104- shutil .rmtree (tmp_workdir )
105+ if CLEANUP_ON_STARTUP :
106+ shutil .rmtree (tmp_workdir , ignore_errors = True )
105107 archive_name = artf_basename + ".zip"
106108
107109 # Create a workdir
@@ -116,41 +118,41 @@ def sign() -> flask.typing.ResponseReturnValue:
116118 os .rename (f .filename , artf_name )
117119
118120 # Calculate the sha256
119- ret_code , _stdout , _sterr = do_shell_exec (sum_cmd .format (tarb = artf_name ))
121+ ret_code , _stdout , _sterr = do_shell_exec (SUM_CMD .format (tarb = artf_name ))
120122 if ret_code == 0 :
121- with open (sha_fname , "w" ) as F :
122- F .write (_stdout )
123+ with open (sha_fname , "w" ) as sha_file :
124+ sha_file .write (_stdout )
123125 sha = shlex .split (_stdout )[0 ]
124126 else :
125127 raise Exception ("Shasum failed!" )
126128
127129 # Sign the tarball
128- ret_code , _stdout , _sterr = do_shell_exec (sign_cmd .format (pasw = pwd , tarb = artf_name ))
130+ ret_code , _stdout , _sterr = do_shell_exec (SIGN_CMD .format (pasw = pwd , tarb = artf_name ))
129131 # If password is incorrect or other error exit.
130132 if ret_code != 0 :
131133 return flask .render_template ("signed.html" ,
132134 artf_name = "Not Authorised" ,
133- artf_url = download_pfix ,
135+ artf_url = DOWNLOAD_PFIX ,
134136 sha = "Not Authorised" ,
135- sha_url = download_pfix ,
137+ sha_url = DOWNLOAD_PFIX ,
136138 sign_name = "Not Authorised" ,
137- sign_url = download_pfix ,
139+ sign_url = DOWNLOAD_PFIX ,
138140 zip_name = "Not Authorised" ,
139- zip_url = download_pfix )
141+ zip_url = DOWNLOAD_PFIX )
140142 # Zip everything
141143 cwd = os .getcwd ()
142144 os .chdir (tmp_workdir )
143145 ret_code , _stdout , _sterr = do_shell_exec (
144- zip_cmd .format (name = artf_basename ))
146+ ZIP_CMD .format (name = artf_basename ))
145147 if ret_code != 0 :
146148 raise Exception ("zip Failed" )
147149 os .chdir (cwd )
148150
149151 # Calculate the download urls
150- sha_url = download_pfix + "/" .join (sha_fname .split ("/" )[1 :])
151- artf_url = download_pfix + "/" .join (artf_name .split ("/" )[1 :])
152- sign_url = download_pfix + "/" .join (sign_fname .split ("/" )[1 :])
153- archive_url = download_pfix + "/" .join (archive_name .split ("/" )[1 :])
152+ sha_url = DOWNLOAD_PFIX + "/" .join (sha_fname .split ("/" )[1 :])
153+ artf_url = DOWNLOAD_PFIX + "/" .join (artf_name .split ("/" )[1 :])
154+ sign_url = DOWNLOAD_PFIX + "/" .join (sign_fname .split ("/" )[1 :])
155+ archive_url = DOWNLOAD_PFIX + "/" .join (archive_name .split ("/" )[1 :])
154156
155157 # Return the results page
156158 return flask .render_template ("signed.html" ,
@@ -163,8 +165,9 @@ def sign() -> flask.typing.ResponseReturnValue:
163165 zip_url = archive_url )
164166
165167
166- @app .route ('/verify' , methods = ['POST' ])
168+ @APP .route ('/verify' , methods = ['POST' ])
167169def verify () -> flask .typing .ResponseReturnValue :
170+ """Invoked on {SERVERIP:PORT}/verify api endpoint."""
168171 if flask .request .method == 'POST' :
169172 # Create a workdir
170173 tmp_workdir = randomise_path ("verification" )
@@ -186,7 +189,7 @@ def verify() -> flask.typing.ResponseReturnValue:
186189
187190 # Verify the archive's signature
188191 ret_code , _stdout , _sterr = do_shell_exec (
189- verify_cmd .format (sig = sig_fname , tarb = artf_fname ))
192+ VERIFY_CMD .format (sig = sig_fname , tarb = artf_fname ))
190193 verified = "Success" if ret_code == 0 else "Failed"
191194
192195 # Return the result
@@ -195,4 +198,4 @@ def verify() -> flask.typing.ResponseReturnValue:
195198 result = _sterr )
196199
197200if __name__ == '__main__' :
198- app .run (host = server_ip , debug = False )
201+ APP .run (host = SERVER_IP , debug = False )
0 commit comments