docs: def gpg_encrypt( fd_in, path_out, sender_key_info, recipient_key_infos, passphrase=None, config_dir=None ):
      """
      Encrypt a stream of data for a set of keys.
      @sender_key_info should be a dict with
      {
          'key_id': ...
          'key_data': ...
          'app_name'; ...
      }
      Return {'status': True} on success
      Return {'error': ...} on error
      """
  
      if config_dir is None:
          config_dir = get_config_dir()
  
      # ingest keys 
      tmpdir = make_gpg_tmphome( prefix="encrypt", config_dir=config_dir )
      for key_info in recipient_key_infos:
          res = gpg_stash_key( "encrypt", key_info['key_data'], config_dir=config_dir, gpghome=tmpdir )
          if res is None:
              shutil.rmtree(tmpdir)
              return {'error': 'Failed to stash key %s' % key_info['key_id']}
  
      # copy over our key
      try:
          sender_privkey = gpg_export_key( sender_key_info['app_name'], sender_key_info['key_id'], include_private=True, config_dir=config_dir )
      except Exception, e:
          log.exception(e)
          shutil.rmtree(tmpdir)
          return {'error': 'No such private key'}
  
      res = gpg_stash_key( "encrypt", sender_privkey, config_dir=config_dir, gpghome=tmpdir )
      if res is None:
          shutil.rmtree(tmpdir)
          return {'error': 'Failed to load sender private key'}
  
      recipient_key_ids = [r['key_id'] for r in recipient_key_infos]
  
      # do the encryption
      gpg = gnupg.GPG( homedir=tmpdir )
      res = gpg.encrypt_file( fd_in, recipient_key_ids, sign=sender_key_info['key_id'], passphrase=passphrase, output=path_out, always_trust=True )
      shutil.rmtree(tmpdir)
  
      if res.status != 'encryption ok':
          log.debug("encrypt_file error: %s" % res.__dict__)
          log.debug("recipients: %s" % recipient_key_ids)
          log.debug("signer: %s" % sender_key_info['key_id'])
          return {'error': 'Failed to encrypt data'}
      
      return {'status': True}