/ RNS / Utilities / rnid.py
rnid.py
  1  #!/usr/bin/env python3
  2  
  3  # Reticulum License
  4  #
  5  # Copyright (c) 2016-2025 Mark Qvist
  6  #
  7  # Permission is hereby granted, free of charge, to any person obtaining a copy
  8  # of this software and associated documentation files (the "Software"), to deal
  9  # in the Software without restriction, including without limitation the rights
 10  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  # copies of the Software, and to permit persons to whom the Software is
 12  # furnished to do so, subject to the following conditions:
 13  #
 14  # - The Software shall not be used in any kind of system which includes amongst
 15  #   its functions the ability to purposefully do harm to human beings.
 16  #
 17  # - The Software shall not be used, directly or indirectly, in the creation of
 18  #   an artificial intelligence, machine learning or language model training
 19  #   dataset, including but not limited to any use that contributes to the
 20  #   training or development of such a model or algorithm.
 21  #
 22  # - The above copyright notice and this permission notice shall be included in
 23  #   all copies or substantial portions of the Software.
 24  #
 25  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 26  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 27  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 28  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 29  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 30  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 31  # SOFTWARE.
 32  
 33  import RNS
 34  import argparse
 35  import time
 36  import sys
 37  import os
 38  import base64
 39  
 40  from RNS._version import __version__
 41  
 42  APP_NAME = "rnid"
 43  
 44  SIG_EXT = "rsg"
 45  ENCRYPT_EXT = "rfe"
 46  CHUNK_SIZE = 16*1024*1024
 47  
 48  def spin(until=None, msg=None, timeout=None):
 49      i = 0
 50      syms = "⢄⢂⢁⡁⡈⡐⡠"
 51      if timeout != None:
 52          timeout = time.time()+timeout
 53  
 54      print(msg+"  ", end=" ")
 55      while (timeout == None or time.time()<timeout) and not until():
 56          time.sleep(0.1)
 57          print(("\b\b"+syms[i]+" "), end="")
 58          sys.stdout.flush()
 59          i = (i+1)%len(syms)
 60  
 61      print("\r"+" "*len(msg)+"  \r", end="")
 62  
 63      if timeout != None and time.time() > timeout:
 64          return False
 65      else:
 66          return True
 67  
 68  def main():
 69      try:
 70          parser = argparse.ArgumentParser(description="Reticulum Identity & Encryption Utility")
 71          # parser.add_argument("file", nargs="?", default=None, help="input file path", type=str)
 72  
 73          parser.add_argument("--config", metavar="path", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
 74          parser.add_argument("-i", "--identity", metavar="identity", action="store", default=None, help="hexadecimal Reticulum identity or destination hash, or path to Identity file", type=str)
 75          parser.add_argument("-g", "--generate", metavar="file", action="store", default=None, help="generate a new Identity")
 76          parser.add_argument("-m", "--import", dest="import_str", metavar="identity_data", action="store", default=None, help="import Reticulum identity in hex, base32 or base64 format", type=str)
 77          parser.add_argument("-x", "--export", action="store_true", default=None, help="export identity to hex, base32 or base64 format")
 78  
 79          parser.add_argument("-v", "--verbose", action="count", default=0, help="increase verbosity")
 80          parser.add_argument("-q", "--quiet", action="count", default=0, help="decrease verbosity")
 81  
 82          parser.add_argument("-a", "--announce", metavar="aspects", action="store", default=None, help="announce a destination based on this Identity")
 83          parser.add_argument("-H", "--hash", metavar="aspects", action="store", default=None, help="show destination hashes for other aspects for this Identity")
 84          parser.add_argument("-e", "--encrypt", metavar="file", action="store", default=None, help="encrypt file")
 85          parser.add_argument("-d", "--decrypt", metavar="file", action="store", default=None, help="decrypt file")
 86          parser.add_argument("-s", "--sign", metavar="path", action="store", default=None, help="sign file")
 87          parser.add_argument("-V", "--validate", metavar="path", action="store", default=None, help="validate signature")
 88  
 89          parser.add_argument("-r", "--read", metavar="file", action="store", default=None, help="input file path", type=str)
 90          parser.add_argument("-w", "--write", metavar="file", action="store", default=None, help="output file path", type=str)
 91          parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
 92          parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # "read input from STDIN instead of file"
 93          parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file", 
 94  
 95          parser.add_argument("-R", "--request", action="store_true", default=False, help="request unknown Identities from the network")
 96          parser.add_argument("-t", action="store", metavar="seconds", type=float, help="identity request timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
 97          parser.add_argument("-p", "--print-identity", action="store_true", default=False, help="print identity info and exit")
 98          parser.add_argument("-P", "--print-private", action="store_true", default=False, help="allow displaying private keys")
 99  
100          parser.add_argument("-b", "--base64", action="store_true", default=False, help="Use base64-encoded input and output")
101          parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output")
102  
103          parser.add_argument("--version", action="version", version="rnid {version}".format(version=__version__))
104          
105          args = parser.parse_args()
106  
107          ops = 0;
108          for t in [args.encrypt, args.decrypt, args.validate, args.sign]:
109              if t:
110                  ops += 1
111          
112          if ops > 1:
113              RNS.log("This utility currently only supports one of the encrypt, decrypt, sign or verify operations per invocation", RNS.LOG_ERROR)
114              exit(1)
115  
116          if not args.read:
117              if args.encrypt:
118                  args.read = args.encrypt
119              if args.decrypt:
120                  args.read = args.decrypt
121              if args.sign:
122                  args.read = args.sign
123  
124          identity_str = args.identity
125          if args.import_str:
126              identity_bytes = None
127              try:
128                  if args.base64:
129                      identity_bytes = base64.urlsafe_b64decode(args.import_str)
130                  elif args.base32:
131                      identity_bytes = base64.b32decode(args.import_str)
132                  else:
133                      identity_bytes = bytes.fromhex(args.import_str)
134              except Exception as e:
135                  print("Invalid identity data specified for import: "+str(e))
136                  exit(41)
137  
138              try:
139                  identity = RNS.Identity.from_bytes(identity_bytes)
140              except Exception as e:
141                  print("Could not create Reticulum identity from specified data: "+str(e))
142                  exit(42)
143  
144              RNS.log("Identity imported")
145              if args.base64:
146                  RNS.log("Public Key  : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
147              elif args.base32:
148                  RNS.log("Public Key  : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
149              else:
150                  RNS.log("Public Key  : "+RNS.hexrep(identity.get_public_key(), delimit=False))
151              if identity.prv:
152                  if args.print_private:
153                      if args.base64:
154                          RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
155                      elif args.base32:
156                          RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
157                      else:
158                          RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
159                  else:
160                      RNS.log("Private Key : Hidden")
161  
162              if args.write:
163                  try:
164                      wp = os.path.expanduser(args.write)
165                      if not os.path.isfile(wp) or args.force:
166                          identity.to_file(wp)
167                          RNS.log("Wrote imported identity to "+str(args.write))
168                      else:
169                          print("File "+str(wp)+" already exists, not overwriting")
170                          exit(43)
171  
172                  except Exception as e:
173                      print("Error while writing imported identity to file: "+str(e))
174                      exit(44)
175  
176              exit(0)
177  
178          if not args.generate and not identity_str:
179              print("\nNo identity provided, cannot continue\n")
180              parser.print_help()
181              print("")
182              exit(2)
183  
184          else:
185              targetloglevel = 4
186              verbosity = args.verbose
187              quietness = args.quiet
188              if verbosity != 0 or quietness != 0:
189                  targetloglevel = targetloglevel+verbosity-quietness
190              
191              # Start Reticulum
192              reticulum = RNS.Reticulum(configdir=args.config, loglevel=targetloglevel)
193              RNS.compact_log_fmt = True
194              if args.stdout:
195                  RNS.loglevel = -1
196  
197              if args.generate:
198                  identity = RNS.Identity()
199                  if not args.force and os.path.isfile(args.generate):
200                      RNS.log("Identity file "+str(args.generate)+" already exists. Not overwriting.", RNS.LOG_ERROR)
201                      exit(3)
202                  else:
203                      try:
204                          identity.to_file(args.generate)
205                          RNS.log(f"New identity {identity} written to {args.generate}")
206                          exit(0)
207                      except Exception as e:
208                          RNS.log("An error ocurred while saving the generated Identity.", RNS.LOG_ERROR)
209                          RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
210                          exit(4)
211  
212              identity = None
213              if len(identity_str) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8*2 and not os.path.isfile(identity_str):
214                  # Try recalling Identity from hex-encoded hash
215                  try:
216                      ident_hash = bytes.fromhex(identity_str)
217                      identity = RNS.Identity.recall(ident_hash) or RNS.Identity.recall(ident_hash, from_identity_hash=True)
218  
219                      if identity == None:
220                          if not args.request:
221                              RNS.log("Could not recall Identity for "+RNS.prettyhexrep(ident_hash)+".", RNS.LOG_ERROR)
222                              RNS.log("You can query the network for unknown Identities with the -R option.", RNS.LOG_ERROR)
223                              exit(5)
224                          else:
225                              RNS.Transport.request_path(ident_hash)
226                              def spincheck():
227                                  return RNS.Identity.recall(ident_hash) != None
228                              spin(spincheck, "Requesting unknown Identity for "+RNS.prettyhexrep(ident_hash), args.t)
229  
230                              if not spincheck():
231                                  RNS.log("Identity request timed out", RNS.LOG_ERROR)
232                                  exit(6)
233                              else:
234                                  identity = RNS.Identity.recall(ident_hash)
235                                  RNS.log("Received Identity "+str(identity)+" for destination "+RNS.prettyhexrep(ident_hash)+" from the network")
236  
237                      else:
238                          ident_str = str(identity)
239                          hash_str  = RNS.prettyhexrep(ident_hash)
240                          if ident_str == hash_str: RNS.log(f"Recalled Identity {ident_str}")
241                          else:                     RNS.log(f"Recalled Identity {ident_str} for destination {hash_str}")
242  
243  
244                  except Exception as e:
245                      RNS.log("Invalid hexadecimal hash provided", RNS.LOG_ERROR)
246                      exit(7)
247  
248                  
249              else:
250                  # Try loading Identity from file
251                  if not os.path.isfile(identity_str):
252                      RNS.log("Specified Identity file not found")
253                      exit(8)
254                  else:
255                      try:
256                          identity = RNS.Identity.from_file(identity_str)
257                          RNS.log("Loaded Identity "+str(identity)+" from "+str(identity_str))
258  
259                      except Exception as e:
260                          RNS.log("Could not decode Identity from specified file")
261                          exit(9)
262  
263              if identity != None:
264                  if args.hash:
265                      try:
266                          aspects = args.hash.split(".")
267                          if not len(aspects) > 0:
268                              RNS.log("Invalid destination aspects specified", RNS.LOG_ERROR)
269                              exit(32)
270                          else:
271                              app_name = aspects[0]
272                              aspects = aspects[1:]
273                              if identity.pub != None:
274                                  destination = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, app_name, *aspects)
275                                  RNS.log("The "+str(args.hash)+" destination for this Identity is "+RNS.prettyhexrep(destination.hash))
276                                  RNS.log("The full destination specifier is "+str(destination))
277                                  time.sleep(0.25)
278                                  exit(0)
279                              else:
280                                  raise KeyError("No public key known")
281                      except Exception as e:
282                          RNS.log("An error ocurred while attempting to send the announce.", RNS.LOG_ERROR)
283                          RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
284  
285                      exit(0)
286  
287                  if args.announce:
288                      try:
289                          aspects = args.announce.split(".")
290                          if not len(aspects) > 1:
291                              RNS.log("Invalid destination aspects specified", RNS.LOG_ERROR)
292                              exit(32)
293                          else:
294                              app_name = aspects[0]
295                              aspects = aspects[1:]
296                              if identity.prv != None:
297                                  destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, app_name, *aspects)
298                                  RNS.log("Created destination "+str(destination))
299                                  RNS.log("Announcing destination "+RNS.prettyhexrep(destination.hash))
300                                  time.sleep(1.1)
301                                  destination.announce()
302                                  time.sleep(0.25)
303                                  exit(0)
304                              else:
305                                  destination = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, app_name, *aspects)
306                                  RNS.log("The "+str(args.announce)+" destination for this Identity is "+RNS.prettyhexrep(destination.hash))
307                                  RNS.log("The full destination specifier is "+str(destination))
308                                  RNS.log("Cannot announce this destination, since the private key is not held")
309                                  time.sleep(0.25)
310                                  exit(33)
311                      except Exception as e:
312                          RNS.log("An error ocurred while attempting to send the announce.", RNS.LOG_ERROR)
313                          RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
314  
315                      exit(0)
316  
317                  if args.print_identity:
318                      if args.base64:
319                          RNS.log("Public Key  : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
320                      elif args.base32:
321                          RNS.log("Public Key  : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
322                      else:
323                          RNS.log("Public Key  : "+RNS.hexrep(identity.get_public_key(), delimit=False))
324                      if identity.prv:
325                          if args.print_private:
326                              if args.base64:
327                                  RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
328                              elif args.base32:
329                                  RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
330                              else:
331                                  RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
332                          else:
333                              RNS.log("Private Key : Hidden")
334                      exit(0)
335  
336                  if args.export:
337                      if identity.prv:
338                              if args.base64:
339                                  RNS.log("Exported Identity : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
340                              elif args.base32:
341                                  RNS.log("Exported Identity : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
342                              else:
343                                  RNS.log("Exported Identity : "+RNS.hexrep(identity.get_private_key(), delimit=False))
344                      else:
345                          RNS.log("Identity doesn't hold a private key, cannot export")
346                          exit(50)
347  
348                      exit(0)
349  
350                  if args.validate:
351                      if not args.read and args.validate.lower().endswith("."+SIG_EXT):
352                          args.read = str(args.validate).replace("."+SIG_EXT, "")
353  
354                      if not os.path.isfile(args.validate):
355                          RNS.log("Signature file "+str(args.read)+" not found", RNS.LOG_ERROR)
356                          exit(10)
357  
358                      if not os.path.isfile(args.read):
359                          RNS.log("Input file "+str(args.read)+" not found", RNS.LOG_ERROR)
360                          exit(11)
361  
362                  data_input = None
363                  if args.read:
364                      if not os.path.isfile(args.read):
365                          RNS.log("Input file "+str(args.read)+" not found", RNS.LOG_ERROR)
366                          exit(12)
367                      else:
368                          try:
369                              data_input = open(args.read, "rb")
370                          except Exception as e:
371                              RNS.log("Could not open input file for reading", RNS.LOG_ERROR)
372                              RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
373                              exit(13)
374  
375                  # TODO: Actually expand this to a good solution
376                  # probably need to create a wrapper that takes
377                  # into account not closing stdin when done
378                  # elif args.stdin:
379                  #     data_input = sys.stdin
380  
381                  data_output = None
382                  if args.encrypt and not args.write and not args.stdout and args.read:
383                      args.write = str(args.read)+"."+ENCRYPT_EXT
384  
385                  if args.decrypt and not args.write and not args.stdout and args.read and args.read.lower().endswith("."+ENCRYPT_EXT):
386                      args.write = str(args.read).replace("."+ENCRYPT_EXT, "")
387  
388                  if args.sign and identity.prv == None:
389                      RNS.log("Specified Identity does not hold a private key. Cannot sign.", RNS.LOG_ERROR)
390                      exit(14)
391  
392                  if args.sign and not args.write and not args.stdout and args.read:
393                      args.write = str(args.read)+"."+SIG_EXT
394  
395                  if args.write:
396                      if not args.force and os.path.isfile(args.write):
397                          RNS.log("Output file "+str(args.write)+" already exists. Not overwriting.", RNS.LOG_ERROR)
398                          exit(15)
399                      else:
400                          try:
401                              data_output = open(args.write, "wb")
402                          except Exception as e:
403                              RNS.log("Could not open output file for writing", RNS.LOG_ERROR)
404                              RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
405                              exit(15)
406                  
407                  # TODO: Actually expand this to a good solution
408                  # probably need to create a wrapper that takes
409                  # into account not closing stdout when done
410                  # elif args.stdout:
411                  #     data_output = sys.stdout
412  
413                  if args.sign:
414                      if identity.prv == None:
415                          RNS.log("Specified Identity does not hold a private key. Cannot sign.", RNS.LOG_ERROR)
416                          exit(16)
417  
418                      if not data_input:
419                          if not args.stdout:
420                              RNS.log("Signing requested, but no input data specified", RNS.LOG_ERROR)
421                          exit(17)
422                      else:
423                          if not data_output:
424                              if not args.stdout:
425                                  RNS.log("Signing requested, but no output specified", RNS.LOG_ERROR)
426                              exit(18)
427  
428                          if not args.stdout:
429                              RNS.log("Signing "+str(args.read))
430                          
431                          try:
432                              data_output.write(identity.sign(data_input.read()))
433                              data_output.close()
434                              data_input.close()
435                              
436                              if not args.stdout:
437                                  if args.read:
438                                      RNS.log("File "+str(args.read)+" signed with "+str(identity)+" to "+str(args.write))
439                                      exit(0)
440  
441                          except Exception as e:
442                              if not args.stdout:
443                                  RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR)
444                                  RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
445                              try:
446                                  data_output.close()
447                              except:
448                                  pass
449                              try:
450                                  data_input.close()
451                              except:
452                                  pass
453                              exit(19)
454  
455                  if args.validate:
456                      if not data_input:
457                          if not args.stdout:
458                              RNS.log("Signature verification requested, but no input data specified", RNS.LOG_ERROR)
459                          exit(20)
460                      else:
461                          # if not args.stdout:
462                          #     RNS.log("Verifying "+str(args.validate)+" for "+str(args.read))
463                          
464                          try:
465                              try:
466                                  sig_input = open(args.validate, "rb")
467                              except Exception as e:
468                                  RNS.log("An error ocurred while opening "+str(args.validate)+".", RNS.LOG_ERROR)
469                                  RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
470                                  exit(21)
471  
472  
473                              validated = identity.validate(sig_input.read(), data_input.read())
474                              sig_input.close()
475                              data_input.close()
476  
477                              if not validated:
478                                  if not args.stdout:
479                                      RNS.log("Signature "+str(args.validate)+" for file "+str(args.read)+" is invalid", RNS.LOG_ERROR)
480                                  exit(22)
481                              else:
482                                  if not args.stdout:
483                                      RNS.log("Signature "+str(args.validate)+" for file "+str(args.read)+" made by Identity "+str(identity)+" is valid")
484                                  exit(0)
485  
486                          except Exception as e:
487                              if not args.stdout:
488                                  RNS.log("An error ocurred while validating signature.", RNS.LOG_ERROR)
489                                  RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
490                              try:
491                                  data_output.close()
492                              except:
493                                  pass
494                              try:
495                                  data_input.close()
496                              except:
497                                  pass
498                              exit(23)
499  
500                  if args.encrypt:
501                      if not data_input:
502                          if not args.stdout:
503                              RNS.log("Encryption requested, but no input data specified", RNS.LOG_ERROR)
504                          exit(24)
505                      else:
506                          if not data_output:
507                              if not args.stdout:
508                                  RNS.log("Encryption requested, but no output specified", RNS.LOG_ERROR)
509                              exit(25)
510  
511                          if not args.stdout:
512                              RNS.log("Encrypting "+str(args.read))
513                          
514                          try:
515                              more_data = True
516                              while more_data:
517                                  chunk = data_input.read(CHUNK_SIZE)
518                                  if chunk:
519                                      data_output.write(identity.encrypt(chunk))
520                                  else:
521                                      more_data = False
522                              data_output.close()
523                              data_input.close()
524                              if not args.stdout:
525                                  if args.read:
526                                      RNS.log("File "+str(args.read)+" encrypted for "+str(identity)+" to "+str(args.write))
527                                      exit(0)
528  
529                          except Exception as e:
530                              if not args.stdout:
531                                  RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR)
532                                  RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
533                              try:
534                                  data_output.close()
535                              except:
536                                  pass
537                              try:
538                                  data_input.close()
539                              except:
540                                  pass
541                              exit(26)
542  
543                  if args.decrypt:
544                      if identity.prv == None:
545                          RNS.log("Specified Identity does not hold a private key. Cannot decrypt.", RNS.LOG_ERROR)
546                          exit(27)
547  
548                      if not data_input:
549                          if not args.stdout:
550                              RNS.log("Decryption requested, but no input data specified", RNS.LOG_ERROR)
551                          exit(28)
552                      else:
553                          if not data_output:
554                              if not args.stdout:
555                                  RNS.log("Decryption requested, but no output specified", RNS.LOG_ERROR)
556                              exit(29)
557  
558                          if not args.stdout:
559                              RNS.log("Decrypting "+str(args.read)+"...")
560                          
561                          try:
562                              more_data = True
563                              while more_data:
564                                  chunk = data_input.read(CHUNK_SIZE)
565                                  if chunk:
566                                      plaintext = identity.decrypt(chunk)
567                                      if plaintext == None:
568                                          if not args.stdout:
569                                              RNS.log("Data could not be decrypted with the specified Identity")
570                                          exit(30)
571                                      else:
572                                          data_output.write(plaintext)
573                                  else:
574                                      more_data = False
575                              data_output.close()
576                              data_input.close()
577                              if not args.stdout:
578                                  if args.read:
579                                      RNS.log("File "+str(args.read)+" decrypted with "+str(identity)+" to "+str(args.write))
580                                      exit(0)
581  
582                          except Exception as e:
583                              if not args.stdout:
584                                  RNS.log("An error ocurred while decrypting data.", RNS.LOG_ERROR)
585                                  RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
586                              try:
587                                  data_output.close()
588                              except:
589                                  pass
590                              try:
591                                  data_input.close()
592                              except:
593                                  pass
594                              exit(31)
595  
596              if True:
597                  pass
598  
599              elif False:
600                  pass
601  
602              else:
603                  print("")
604                  parser.print_help()
605                  print("")
606  
607      except KeyboardInterrupt:
608          print("")
609          exit(255)
610  
611  if __name__ == "__main__":
612      main()