Gnumed.pycommon.gmCrypto

Package Gnumed :: Package pycommon :: Module gmCrypto |
[frames] | no frames]

# Source Code for Module Gnumed.pycommon.gmCrypto

      1  # -*- coding: utf-8 -*-
      2
      3  __doc__ = """GNUmed crypto tools.
      4
      5  First and only rule:
      6
      7          DO NOT REIMPLEMENT ENCRYPTION
      8
      9          Use existing tools.
     10  """
     11  #===========================================================================
     12  __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
     13  __license__ = "GPL v2 or later (details at http://www.gnu.org)"
     14
     15  # std libs
     16  import sys
     17  import os
     18  import logging
     19  import tempfile
     20
     21
     22  # GNUmed libs
     23  if __name__ == '__main__':
     24          sys.path.insert(0, '../../')
     25  from Gnumed.pycommon import gmLog2
     26  from Gnumed.pycommon import gmShellAPI
     27  from Gnumed.pycommon import gmTools
     28  from Gnumed.pycommon import gmMimeLib
     29
     30
     31  _log = logging.getLogger('gm.encryption')
     32
     33  #===========================================================================
     34  # archiving methods
     35  #---------------------------------------------------------------------------
     36 -def create_encrypted_zip_archive_from_dir(source_dir, comment=None, overwrite=True, passphrase=None, verbose=False):
     37          """Use 7z to create an encrypted ZIP archive of a directory.
     38
     39          <source_dir>            will be included into the archive
     40          <comment>                       included as a file containing the comment
     41          <overwrite>                     remove existing archive before creation, avoiding
     42                                                  *updating* of those, and thereby including unintended data
     43          <passphrase>            minimum length of 5
     44
     45          The resulting zip archive will always be named
     46          "datawrapper.zip" for confidentiality reasons. If callers
     47          want another name they will have to shutil.move() the zip
     48          file themselves. This archive will be compressed and
     49          AES256 encrypted with the given passphrase. Therefore,
     50          the result will not decrypt with earlier versions of
     51          unzip software. On Windows, 7z oder WinZip are needed.
     52
     53          The zip format does not support header encryption thereby
     54          allowing attackers to gain knowledge of patient details
     55          by observing the names of files and directories inside
     56          the encrypted archive.
     57
     58          To reduce that attack surface, GNUmed will create
     59          _another_ zip archive inside "datawrapper.zip", which
     60          eventually wraps up the patient data as "data.zip". That
     61          archive is not compressed and not encrypted, and can thus
     62          be unpacked with any old unzipper.
     63
     64          Note that GNUmed does NOT remember the passphrase for
     65          you. You will have to take care of that yourself, and
     66          possibly also safely hand over the passphrase to any
     67          receivers of the zip archive.
     68          """
     69          if len(passphrase) < 5:
     70                  _log.error('<passphrase> must be at least 5 characters/signs/digits')
     71                  return None
     72          gmLog2.add_word2hide(passphrase)
     73
     74          source_dir = os.path.abspath(source_dir)
     75          if not os.path.isdir(source_dir):
     76                  _log.error('<source_dir> does not exist or is not a directory: %s', source_dir)
     77                  return False
     78
     79          for cmd in ['7z', '7z.exe']:
     80                  found, binary = gmShellAPI.detect_external_binary(binary = cmd)
     81                  if found:
     82                          break
     83          if not found:
     84                  _log.warning('no 7z binary found')
     85                  return None
     86
     87          sandbox_dir = gmTools.mk_sandbox_dir()
     88          archive_path_inner = os.path.join(sandbox_dir, 'data')
     89          if not gmTools.mkdir(archive_path_inner):
     90                  _log.error('cannot create scratch space for inner achive: %s', archive_path_inner)
     91          archive_fname_inner = 'data.zip'
     92          archive_name_inner = os.path.join(archive_path_inner, archive_fname_inner)
     93          archive_path_outer = gmTools.gmPaths().tmp_dir
     94          archive_fname_outer = 'datawrapper.zip'
     95          archive_name_outer = os.path.join(archive_path_outer, archive_fname_outer)
     96          # remove existing archives so they don't get *updated* rather than newly created
     97          if overwrite:
     98                  if not gmTools.remove_file(archive_name_inner, force = True):
     99                          _log.error('cannot remove existing archive [%s]', archive_name_inner)
    100                          return False
    101
    102                  if not gmTools.remove_file(archive_name_outer, force = True):
    103                          _log.error('cannot remove existing archive [%s]', archive_name_outer)
    104                          return False
    105
    106          # 7z does not support ZIP comments so create a text file holding the comment
    107          if comment is not None:
    108                  tmp, fname = os.path.split(source_dir.rstrip(os.sep))
    109                  comment_filename = os.path.join(sandbox_dir, '000-%s-comment.txt' % fname)
    110                  with open(comment_filename, mode = 'wt', encoding = 'utf8', errors = 'replace') as comment_file:
    111                          comment_file.write(comment)
    112
    113          # create inner (data) archive: uncompressed, unencrypted, similar to a tar archive
    114          args = [
    115                  binary,
    116                  'a',                            # create archive
    117                  '-sas',                         # be smart about archive name extension
    118                  '-bd',                          # no progress indicator
    119                  '-mx0',                         # no compression (only store files)
    120                  '-mcu=on',                      # UTF8 filenames
    121                  '-l',                           # store content of links, not links
    122                  '-scsUTF-8',            # console charset
    123                  '-tzip'                         # force ZIP format
    124          ]
    125          if verbose:
    126                  args.append('-bb3')
    127                  args.append('-bt')
    128          else:
    129                  args.append('-bb1')
    130          args.append(archive_name_inner)
    131          args.append(source_dir)
    132          if comment is not None:
    133                  args.append(comment_filename)
    134          success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose)
    135          if not success:
    136                  _log.error('cannot create inner archive')
    137                  return None
    138
    139          # create "decompress instructions" file
    140          instructions_filename = os.path.join(archive_path_inner, '000-on_Windows-open_with-WinZip_or_7z_tools')
    141          open(instructions_filename, mode = 'wt').close()
    142
    143          # create outer (wrapper) archive: compressed, encrypted
    144          args = [
    145                  binary,
    146                  'a',                                    # create archive
    147                  '-sas',                                 # be smart about archive name extension
    148                  '-bd',                                  # no progress indicator
    149                  '-mx9',                                 # best available zip compression ratio
    150                  '-mcu=on',                              # UTF8 filenames
    151                  '-l',                                   # store content of links, not links
    152                  '-scsUTF-8',                    # console charset
    153                  '-tzip',                                # force ZIP format
    154                  '-mem=AES256',                  # force useful encryption
    155                  '-p%s' % passphrase             # set passphrase
    156          ]
    157          if verbose:
    158                  args.append('-bb3')
    159                  args.append('-bt')
    160          else:
    161                  args.append('-bb1')
    162          args.append(archive_name_outer)
    163          args.append(archive_path_inner)
    164          success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose)
    165          if success:
    166                  return archive_name_outer
    167          _log.error('cannot create outer archive')
    168          return None
    169
    170  #---------------------------------------------------------------------------
    171 -def create_zip_archive_from_dir(source_dir, archive_name=None, comment=None, overwrite=True, verbose=False):
    172
    173          source_dir = os.path.abspath(source_dir)
    174          if not os.path.isdir(source_dir):
    175                  _log.error('<source_dir> does not exist or is not a directory: %s', source_dir)
    176                  return False
    177
    178          for cmd in ['7z', '7z.exe']:
    179                  found, binary = gmShellAPI.detect_external_binary(binary = cmd)
    180                  if found:
    181                          break
    182          if not found:
    183                  _log.warning('no 7z binary found')
    184                  return None
    185
    186          if archive_name is None:
    187                  # do not assume we can write to "sourcedir/../"
    188                  archive_path = gmTools.gmPaths().tmp_dir
    189                  # but do take archive name from source_dir
    190                  tmp, archive_fname = os.path.split(source_dir.rstrip(os.sep) + '.zip')
    191                  archive_name = os.path.join(archive_path, archive_fname)
    192          # remove any existing archives so they don't get *updated*
    193          # rather than newly created
    194          if overwrite:
    195                  if not gmTools.remove_file(archive_name, force = True):
    196                          _log.error('cannot remove existing archive [%s]', archive_name)
    197                          return False
    198          # 7z does not support ZIP comments so create
    199          # a text file holding the comment ...
    200          if comment is not None:
    201                  comment_filename = os.path.abspath(archive_name) + '.comment.txt'
    202                  if gmTools.remove_file(comment_filename, force = True):
    203                          with open(comment_filename, mode = 'wt', encoding = 'utf8', errors = 'replace') as comment_file:
    204                                  comment_file.write(comment)
    205                  else:
    206                          _log.error('cannot remove existing archive comment file [%s]', comment_filename)
    207                          comment = None
    208
    209          # compress
    210          args = [
    211                  binary,
    212                  'a',                            # create archive
    213                  '-sas',                         # be smart about archive name extension
    214                  '-bd',                          # no progress indicator
    215                  '-mx9',                         # best available zip compression ratio
    216                  '-mcu=on',                      # UTF8 filenames
    217                  '-l',                           # store content of links, not links
    218                  '-scsUTF-8',            # console charset
    219                  '-tzip'                         # force ZIP format
    220          ]
    221          if verbose:
    222                  args.append('-bb3')
    223                  args.append('-bt')
    224          else:
    225                  args.append('-bb1')
    226          args.append(archive_name)
    227          args.append(source_dir)
    228          if comment is not None:
    229                  args.append(comment_filename)
    230          success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose)
    231          if comment is not None:
    232                  gmTools.remove_file(comment_filename)
    233          if success:
    234                  return archive_name
    235
    236          return None
    237
    238  #===========================================================================
    239  # file decryption methods
    240  #---------------------------------------------------------------------------
    241 -def gpg_decrypt_file(filename=None, verbose=False, target_ext=None):
    242          """The system is expected to be set up for safely getting the
    243             passphrase from the user, typically via gpg-agent.
    244          """
    245          assert (filename is not None), '<filename> must not be None'
    246
    247          _log.debug('attempting GPG decryption')
    248          for cmd in ['gpg2', 'gpg', 'gpg2.exe', 'gpg.exe']:
    249                  found, binary = gmShellAPI.detect_external_binary(binary = cmd)
    250                  if found:
    251                          break
    252          if not found:
    253                  _log.warning('no gpg binary found')
    254                  return None
    255
    256          basename = os.path.splitext(filename)[0]
    257          filename_decrypted = gmTools.get_unique_filename(prefix = '%s-decrypted-' % basename, suffix = target_ext)
    258          args = [
    259                  binary,
    260                  '--utf8-strings',
    261                  '--display-charset', 'utf-8',
    262                  '--batch',
    263                  '--no-greeting',
    264                  '--enable-progress-filter',
    265                  '--decrypt',
    266                  '--output', filename_decrypted
    267                  ##'--use-embedded-filename'                             # not all encrypted files carry a filename
    268          ]
    269          if verbose:
    270                  args.extend ([
    271                          '--verbose', '--verbose',
    272                          '--debug-level', '8',
    273                          '--debug', 'packet,mpi,crypto,filter,iobuf,memory,cache,memstat,trust,hashing,clock,lookup,extprog'
    274                          ##'--debug-all',                                                # will log passphrase
    275                          ##'--debug, 'ipc',                                              # will log passphrase
    276                          ##'--debug-level', 'guru',                              # will log passphrase
    277                          ##'--debug-level', '9',                                 # will log passphrase
    278                  ])
    279          args.append(filename)
    280          success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = verbose, encoding = 'utf-8')
    281          if success:
    282                  return filename_decrypted
    283          return None
    284
    285  #===========================================================================
    286  # file encryption methods
    287  #---------------------------------------------------------------------------
    288 -def gpg_encrypt_file_symmetric(filename=None, comment=None, verbose=False, passphrase=None, remove_unencrypted=False):
    289
    290          #add short decr instr to comment
    291          assert (filename is not None), '<filename> must not be None'
    292
    293          _log.debug('attempting symmetric GPG encryption')
    294          for cmd in ['gpg2', 'gpg', 'gpg2.exe', 'gpg.exe']:
    295                  found, binary = gmShellAPI.detect_external_binary(binary = cmd)
    296                  if found:
    297                          break
    298          if not found:
    299                  _log.warning('no gpg binary found')
    300                  return None
    301          filename_encrypted = filename + '.asc'
    302          args = [
    303                  binary,
    304                  '--utf8-strings',
    305                  '--display-charset', 'utf-8',
    306                  '--batch',
    307                  '--no-greeting',
    308                  '--enable-progress-filter',
    309                  '--symmetric',
    310                  '--cipher-algo', 'AES256',
    311                  '--armor',
    312                  '--output', filename_encrypted
    313          ]
    314          if comment is not None:
    315                  args.extend(['--comment', comment])
    316          if verbose:
    317                  args.extend ([
    318                          '--verbose', '--verbose',
    319                          '--debug-level', '8',
    320                          '--debug', 'packet,mpi,crypto,filter,iobuf,memory,cache,memstat,trust,hashing,clock,lookup,extprog',
    321                          ##'--debug-all',                                                # will log passphrase
    322                          ##'--debug, 'ipc',                                              # will log passphrase
    323                          ##'--debug-level', 'guru',                              # will log passphrase
    324                          ##'--debug-level', '9',                                 # will log passphrase
    325                  ])
    326          pwd_fname = None
    327          if passphrase is not None:
    328                  pwd_file = tempfile.NamedTemporaryFile(mode = 'w+t', encoding = 'utf8', delete = False)
    329                  pwd_fname = pwd_file.name
    330                  args.extend ([
    331                          '--pinentry-mode', 'loopback',
    332                          '--passphrase-file', pwd_fname
    333                  ])
    334                  pwd_file.write(passphrase)
    335                  pwd_file.close()
    336          args.append(filename)
    337          try:
    338                  success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = verbose, encoding = 'utf-8')
    339          finally:
    340                  if pwd_fname is not None:
    341                          os.remove(pwd_fname)
    342          if not success:
    343                  return None
    344          if not remove_unencrypted:
    345                  return filename_encrypted
    346          if gmTools.remove_file(filename):
    347                  return filename_encrypted
    348          gmTools.remove_file(filename_encrypted)
    349          return None
    350
    351  #---------------------------------------------------------------------------
    352 -def aes_encrypt_file(filename=None, passphrase=None, comment=None, verbose=False, remove_unencrypted=False):
    353          assert (filename is not None), '<filename> must not be None'
    354          assert (passphrase is not None), '<passphrase> must not be None'
    355
    356          if len(passphrase) < 5:
    357                  _log.error('<passphrase> must be at least 5 characters/signs/digits')
    358                  return None
    359          gmLog2.add_word2hide(passphrase)
    360
    361          #add 7z/winzip url to comment.txt
    362          _log.debug('attempting 7z AES encryption')
    363          for cmd in ['7z', '7z.exe']:
    364                  found, binary = gmShellAPI.detect_external_binary(binary = cmd)
    365                  if found:
    366                          break
    367          if not found:
    368                  _log.warning('no 7z binary found, trying gpg')
    369                  return None
    370
    371          if comment is not None:
    372                  archive_path, archive_name = os.path.split(os.path.abspath(filename))
    373                  comment_filename = gmTools.get_unique_filename (
    374                          prefix = '%s.7z.comment-' % archive_name,
    375                          tmp_dir = archive_path,
    376                          suffix = '.txt'
    377                  )
    378                  with open(comment_filename, mode = 'wt', encoding = 'utf8', errors = 'replace') as comment_file:
    379                          comment_file.write(comment)
    380          else:
    381                  comment_filename = ''
    382          filename_encrypted = '%s.7z' % filename
    383          args = [binary, 'a', '-bb3', '-mx0', "-p%s" % passphrase, filename_encrypted, filename, comment_filename]
    384          encrypted, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose)
    385          gmTools.remove_file(comment_filename)
    386          if not encrypted:
    387                  return None
    388          if not remove_unencrypted:
    389                  return filename_encrypted
    390          if gmTools.remove_file(filename):
    391                  return filename_encrypted
    392          gmTools.remove_file(filename_encrypted)
    393          return None
    394
    395  #---------------------------------------------------------------------------
    396 -def encrypt_pdf(filename=None, passphrase=None, verbose=False, remove_unencrypted=False):
    397          assert (filename is not None), '<filename> must not be None'
    398          assert (passphrase is not None), '<passphrase> must not be None'
    399
    400          if len(passphrase) < 5:
    401                  _log.error('<passphrase> must be at least 5 characters/signs/digits')
    402                  return None
    403
    404          gmLog2.add_word2hide(passphrase)
    405          _log.debug('attempting PDF encryption')
    406          for cmd in ['qpdf', 'qpdf.exe']:
    407                  found, binary = gmShellAPI.detect_external_binary(binary = cmd)
    408                  if found:
    409                          break
    410          if not found:
    411                  _log.warning('no qpdf binary found')
    412                  return None
    413
    414          filename_encrypted = '%s.encrypted.pdf' % os.path.splitext(filename)[0]
    415          args = [
    416                  binary,
    417                  '--verbose',
    418                  '--encrypt', passphrase, '', '128',
    419                  '--print=full', '--modify=none', '--extract=n',
    420                  '--use-aes=y',
    421                  '--',
    422                  filename,
    423                  filename_encrypted
    424          ]
    425          success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose)
    426          if not success:
    427                  return None
    428
    429          if not remove_unencrypted:
    430                  return filename_encrypted
    431
    432          if gmTools.remove_file(filename):
    433                  return filename_encrypted
    434
    435          gmTools.remove_file(filename_encrypted)
    436          return None
    437
    438  #---------------------------------------------------------------------------
    439 -def encrypt_file_symmetric(filename=None, passphrase=None, comment=None, verbose=False, remove_unencrypted=False, convert2pdf=False):
    440          """Encrypt <filename> with a symmetric cipher.
    441
    442          <convert2pdf> - True: convert <filename> to PDF, if possible, and encrypt that.
    443          """
    444          assert (filename is not None), '<filename> must not be None'
    445
    446          if convert2pdf:
    447                  _log.debug('PDF encryption preferred, attempting conversion if needed')
    448                  pdf_fname = gmMimeLib.convert_file (
    449                          filename = filename,
    450                          target_mime = 'application/pdf',
    451                          target_filename = filename + '.pdf',
    452                          verbose = verbose
    453                  )
    454                  if pdf_fname is not None:
    455                          _log.debug('successfully converted to PDF')
    456                          # remove non-pdf file
    457                          gmTools.remove_file(filename)
    458                          filename = pdf_fname
    459
    460          # try PDF-inherent AES
    461          encrypted_filename = encrypt_pdf (
    462                  filename = filename,
    463                  passphrase = passphrase,
    464                  verbose = verbose,
    465                  remove_unencrypted = remove_unencrypted
    466          )
    467          if encrypted_filename is not None:
    468                  return encrypted_filename
    469
    470          # try 7z based AES
    471          encrypted_filename = aes_encrypt_file (
    472                  filename = filename,
    473                  passphrase = passphrase,
    474                  comment = comment,
    475                  verbose = verbose,
    476                  remove_unencrypted = remove_unencrypted
    477          )
    478          if encrypted_filename is not None:
    479                  return encrypted_filename
    480
    481          # try GPG based AES
    482          return gpg_encrypt_file_symmetric(filename = filename, passphrase = passphrase, comment = comment, verbose = verbose, remove_unencrypted = remove_unencrypted)
    483
    484  #---------------------------------------------------------------------------
    485 -def encrypt_file(filename=None, receiver_key_ids=None, passphrase=None, comment=None, verbose=False, remove_unencrypted=False, convert2pdf=False):
    486          """Encrypt an arbitrary file.
    487
    488          <remove_unencrypted>
    489                  True: remove unencrypted source file if encryption succeeded
    490          <convert2pdf>
    491                  True: attempt conversion to PDF of input file before encryption
    492                          success: the PDF is encrypted (and the non-PDF source file is removed)
    493                          failure: the source file is encrypted
    494          """
    495          assert (filename is not None), '<filename> must not be None'
    496
    497          # cannot do asymmetric
    498          if receiver_key_ids is None:
    499                  _log.debug('no receiver key IDs: cannot try asymmetric encryption')
    500                  return encrypt_file_symmetric (
    501                          filename = filename,
    502                          passphrase = passphrase,
    503                          comment = comment,
    504                          verbose = verbose,
    505                          remove_unencrypted = remove_unencrypted,
    506                          convert2pdf = convert2pdf
    507                  )
    508
    509          # asymmetric not implemented yet
    510          return None
    511
    512  #---------------------------------------------------------------------------
    513 -def encrypt_directory_content(directory=None, receiver_key_ids=None, passphrase=None, comment=None, verbose=False, remove_unencrypted=True, convert2pdf=False):
    514          assert (directory is not None), 'source <directory> must not be None'
    515          _log.debug('encrypting content of [%s]', directory)
    516          try:
    517                  items = os.listdir(directory)
    518          except OSError:
    519                  return False
    520
    521          for item in items:
    522                  full_item = os.path.join(directory, item)
    523                  if os.path.isdir(full_item):
    524                          subdir_encrypted = encrypt_directory_content (
    525                                  directory = full_item,
    526                                  receiver_key_ids = receiver_key_ids,
    527                                  passphrase = passphrase,
    528                                  comment = comment,
    529                                  verbose = verbose
    530                          )
    531                          if subdir_encrypted is False:
    532                                  return False
    533                          continue
    534
    535                  fname_encrypted = encrypt_file (
    536                          filename = full_item,
    537                          receiver_key_ids = receiver_key_ids,
    538                          passphrase = passphrase,
    539                          comment = comment,
    540                          verbose = verbose,
    541                          remove_unencrypted = remove_unencrypted,
    542                          convert2pdf = convert2pdf
    543                  )
    544                  if fname_encrypted is None:
    545                          return False
    546
    547          return True
    548
    549  #===========================================================================
    550  # file anonymization methods
    551  #---------------------------------------------------------------------------
    552 -def anonymize_file(filename):
    553          assert (filename is not None), '<filename> must not be None'
    554
    555
    556
    557
    558  #===========================================================================
    559  # main
    560  #---------------------------------------------------------------------------
    561  if __name__ == '__main__':
    562
    563          if len(sys.argv) < 2:
    564                  sys.exit()
    565
    566          if sys.argv[1] != 'test':
    567                  sys.exit()
    568
    569          # for testing:
    570          logging.basicConfig(level = logging.DEBUG)
    571          from Gnumed.pycommon import gmI18N
    572          gmI18N.activate_locale()
    573          gmI18N.install_domain()
    574
    575          #-----------------------------------------------------------------------
    576 -        def test_gpg_decrypt():
    577                  print(gpg_decrypt_file(filename = sys.argv[2], verbose = True))
    578
    579          #-----------------------------------------------------------------------
    580 -        def test_gpg_encrypt_symmetric():
    581                  print(gpg_encrypt_file_symmetric(filename = sys.argv[2], passphrase = sys.argv[3], verbose = True, comment = 'GNUmed testing'))
    582
    583          #-----------------------------------------------------------------------
    584 -        def test_aes_encrypt():
    585                  print(aes_encrypt_file(filename = sys.argv[2], passphrase = sys.argv[3], comment = sys.argv[4], verbose = True))
    586
    587          #-----------------------------------------------------------------------
    588 -        def test_encrypt_pdf():
    589                  print(encrypt_pdf(filename = sys.argv[2], passphrase = sys.argv[3], verbose = True))
    590
    591          #-----------------------------------------------------------------------
    592 -        def test_encrypt_file():
    593                  print(encrypt_file(filename = sys.argv[2], passphrase = sys.argv[3], verbose = True, convert2pdf = True))
    594
    595          #-----------------------------------------------------------------------
    596 -        def test_zip_archive_from_dir():
    597                  print(create_zip_archive_from_dir (
    598                          sys.argv[2],
    599                          #archive_name=None,
    600                          comment = 'GNUmed test archive',
    601                          overwrite = True,
    602                          verbose = True
    603                  ))
    604
    605          #-----------------------------------------------------------------------
    606 -        def test_encrypted_zip_archive_from_dir():
    607                  print(create_encrypted_zip_archive_from_dir (
    608                          sys.argv[2],
    609                          comment = 'GNUmed test archive',
    610                          overwrite = True,
    611                          passphrase = sys.argv[3],
    612                          verbose = True
    613                  ))
    614
    615          #-----------------------------------------------------------------------
    616          # encryption
    617          #test_aes_encrypt()
    618          test_encrypt_pdf()
    619          #test_gpg_encrypt_symmetric()
    620          #test_encrypt_file()
    621
    622          # decryption
    623          #test_gpg_decrypt()
    624
    625          #test_zip_archive_from_dir()
    626          #test_encrypted_zip_archive_from_dir()
    627