Package Gnumed :: Package wxpython :: Module gmCryptoText |
[frames] | no frames]

# Source Code for Module Gnumed.wxpython.gmCryptoText

      1  #!/usr/bin/python
      2  #############################################################################
      3  #
      4  # gmCryptoText - implements a "crypto" aware text widget
      5  # ---------------------------------------------------------------------------
      6  # This text widget allows arbitrary text to be entered via keyboard,
      7  # cut'n paste via clipboard, or text files via drag'n drop
      8  # Right clicking pops up a menu that allows to encrypt or decrypt
      9  # the selected text segment.
     10  #
     11  # @author: Dr. Horst Herb
     12  # @copyright: author
     13  # @license: GPL v2 or later (details at http://www.gnu.org)
     14  # @dependencies: wxPython (>= version 2.3.1)
     15  # @change log:
     16  #       30.06.2001 hherb initial implementation, untested
     17  #       25.10.2001 commenting of source, module test enabled, debug log inserts removed
     18  #
     19  # @TODO:
     20  #       - all runtime error checking / exception handling
     21  #       - plug in structure for ciphers, rich popup menu selection of crypto methods
     22  #       - use Python OpenSSL wrappers or GnuPG wrapper!
     23  #       - tagging of the used cipher and the user within the encrypted text
     24  #       - timer that expires pas phrase after arbitrary time intervals
     25  #       - implement a "rich text" widget
     26  ############################################################################
     27
     28
     29  """This module implements a ""crypto"" aware text widget
     30
     31  This text widget allows arbitrary text to be entered via keyboard,
     32  cut'n paste via clipboard, or text files via drag'n drop
     33  Right clicking pops up a menu that allows to encrypt or decrypt
     34  the selected text segment.
     35  """
     36
     37  import wx
     38
     39  import string, rotor, binascii
     40
     41  ID_POP_ENCRYPT = wx.NewId()
     42  ID_POP_DECRYPT = wx.NewId()
     43  ID_POP_PASSPHRASE = wx.NewId()
     44
     45 -class gmTextctrlFileDropTarget(wx.FileDropTarget):
     46      """ a generic text control widget that accepts dropped files """
     47
     48 -    def __init__(self, textwindow):
     49          wx.FileDropTarget.__init__(self)
     50          self.textwindow=textwindow
     51
     52 -    def OnDropFiles(self, x, y, filenames):
     53          """inserts the dropped file(s) content(s) at the cursor position"""
     54          for file in filenames:
     55              self.textwindow.WriteText(string.join(open(file, 'r').readlines()))
     56
     57
     58 -class gmCryptoText(wx.TextCtrl):
     59      """A special text widget that supports cryptography
     60
     61      A right mouse click pops up a manu that allows to encrypt
     62      or decrypt selected text segments.
     63      You can drag and drop any number of text files into the text
     64      widget, and that text will be inserted at the current cursor
     65      position
     66      """
     67
     68 -    def __init__(self, parent, id, size=wx.DefaultSize, style=wx.TE_MULTILINE|wx.TE_RICH, defaulttext=None):
     69          #initialize parent class
     70          wx.TextCtrl.__init__(self, parent, id, size=size, style=style)
     71          self.SetDefaultStyle(wx.TextAttr(wx.RED))
     72
     73          #will search for text tags within fuzzymargin characters
     74          self.fuzzymargin = 25
     75          self.passphrase = None
     76          #time in seconds until passphrase expires
     77          self. passphrase_expiry = 120
     78          #the text selected for encryption/decryption
     79          self.textselection = None
     80          self. selectionStart = 0
     81          self.selectionEnd = 0
     82
     83          if defaulttext is not None:
     84                  self.WriteText(defaulttext)
     85
     86          #a reserved ID for events related to this widget
     87          self.aID = wx.NewId()
     88
     89          #make this text widget a drop target for drag&dropped text files
     90          dt = gmTextctrlFileDropTarget(self)
     91          self.SetDropTarget(dt)
     92
     93          #bugger... this one for wxGTK
     94          wx.EVT_RIGHT_UP(self,self.OnRightClick)
     95          wx.EVT_RIGHT_DOWN(self, self.OnRightDown)
     96
     97          #...and this one for wxMSW (hope this inconsistency is fixed soon
     98          #wx.EVT_COMMAND_RIGHT_CLICK(self, self.aID, self.OnRightClick)
     99
    100 -    def OnRightClick(self, event):
    101          "A right mouse click triggers a popup menu for cryptographic functionality"
    102
    103          self.selectionStart, self.selectionEnd = self.GetSelection()
    104
    105          #create a popup menu
    106          menu = wx.Menu()
    107          menu.Append(ID_POP_ENCRYPT, _("Encrypt"))
    108          menu.Append(ID_POP_DECRYPT, _("Decrypt"))
    109          menu.Append(ID_POP_PASSPHRASE, _("Set pass phrase"))
    110
    111          #connect the events to event handler functions
    112          wx.EVT_MENU(self, ID_POP_ENCRYPT, self.OnEncrypt)
    113          wx.EVT_MENU(self, ID_POP_DECRYPT, self.OnDecrypt)
    114          wx.EVT_MENU(self, ID_POP_PASSPHRASE, self.OnSetPassphrase)
    115
    116          #show the menu
    117          self.PopupMenu(menu, wxPoint(event.GetX(), event.GetY()))
    118
    119          #free resources
    120          menu.DestroyLater()
    121
    122          #anybody else needs to intercept right click events?
    123          event.Skip()
    124
    125
    126          def OnContextMenu(self, event):
    127                  pass
    128
    129
    130 -    def OnEncrypt(self, event):
    131          """triggered by popup contect menu event"""
    132
    133          #get the selected text if any
    134          self.textselection = self.GetValue()[self.selectionStart:self.selectionEnd]
    135          #anything to do?
    136          if len(self.textselection)<1:
    137              return
    138
    139          #we can't crypt without passphrase, so ask for it if needed!
    140          if self.passphrase is None:
    141              self.passphrase = self.AskForPassphrase()
    142              if self.passphrase == None:
    143                  return
    144          #In order to be displayed, binary crypt output has to be 'hexlified'
    145          #encrypted text is tagged with <! ... !>
    146          #future versions will embed a algorithm tag here
    147          self.Replace(self.selectionStart, self.selectionEnd, \
    148                       '<!' + self.GetIdentTag() + binascii.hexlify(self.Encrypt(self.textselection, self.passphrase)) + '!>')
    149
    150
    151 -    def OnDecrypt(self, event):
    152
    153          if self.passphrase is None:
    154              self.passphrase = self.AskForPassphrase()
    155              if self.passphrase == None:
    156                  return
    157
    158          textselection, self.selectionStart, self.selectionEnd = \
    159              self.FuzzyScanSelection(self.selectionStart, self.selectionEnd, self.fuzzymargin)
    160          #is the selection tagged as encrypted ?
    161          if textselection[:2] != '<!' or textselection[-2:] != '!>':
    162              wx.MessageBox(_("This is not correctly encrypted text!"))
    163              return
    164          #get rid of the tags
    165          textselection = textselection[2:-2]
    166          identtag, textselection = self.StripIdentTag(textselection)
    167          #self.textselection = self.textselection[len(identtag):]
    168          #and don't forget to unhexlify the ciphertext before you feed it to the crypt
    169          decoded = self.Decrypt(binascii.unhexlify(textselection), self.passphrase, identtag)
    170          self.Replace(self.selectionStart, self.selectionEnd, decoded)
    171
    172
    173 -    def OnSetPassphrase(self, event):
    174          self.passphrase = self.AskForPassphrase()
    175
    176
    177 -    def OnRightDown(self, event):
    178          """dummy function; if this event was not intercepted, GTK would
    179          clear the text selection the very moment the mouse button is clicked"""
    180          pass
    181
    182 -    def AskForPassphrase(self):
    183          """asks for a pass phrase and returns it"""
    184          dlg = wxTextEntryDialog(self, _("Please enter your pass phrase:"), _("Pass phrase expired"), style=wxOK|wxCANCEL|wx.CENTRE|wx.TE_PASSWORD)
    185          if dlg.ShowModal() == wx.ID_OK:
    186              retval = dlg.GetValue()
    187          else:
    188              retval = None
    189          dlg.DestroyLater()
    190          return retval
    191
    192
    193 -    def Encrypt(self, cleartext, key):
    194          """override this function for your own crypto funcs"""
    195          rt = rotor.newrotor(key, 12)
    196          return rt.encrypt(cleartext)
    197
    198
    199 -    def Decrypt(self, ciphertext, key, identtag):
    200          """override this function for your own crypto funcs"""
    201          rt = rotor.newrotor(key, 12)
    202          return rt.decrypt(ciphertext)
    203
    204
    205 -    def StripIdentTag(self, text):
    206          """Remove the 'ident tag' from text and return both tag and test"""
    207          if text[0] != '[':
    208              "No ident tag ?"
    209              return '', text
    210          try:
    211              endtag = string.index(text, ']')+1
    212          except ValueError:
    213              return '', text
    214          return text[:endtag], text[endtag:]
    215
    216
    217 -    def GetIdentTag(self):
    218          """This is a 'virtual' function which should be overridden to provide your own meaningful tag"""
    219          return '[rotor]'
    220
    221
    222 -    def SetFuzzyMargin(self, margin):
    223          """The fuzzy margin is the number of characters on each side of the text selection
    224          the decryption algorithm will search for correct delimiters. It should be at least as long as
    225          the IdentTag is plus an extra 3 characters to allow for the crypto tag"""
    226          self.fuzzymargin = margin
    227
    228
    229 -    def FuzzyScanSelection(self, frompos, topos, margin):
    230          fulltext = self.GetValue()
    231          #search left margin
    232          start = frompos - margin
    233          if start < 0: start = 0
    234          if frompos == 0: frompos = 1
    235          #search right margin
    236          finish = topos + margin
    237          if finish > len(fulltext): finish = len(fulltext)
    238          if topos > len(fulltext)-2: topos = len (fulltext)-2
    239          try:
    240              left = string.rindex(fulltext, '<', start, frompos)
    241              right = string.index(fulltext, '>', topos, finish)+1
    242          except ValueError:
    243              wx.LogMessage("FuzzyScan went wrong")
    244              return ''
    245          return fulltext[left:right], left,right
    246
    247  #############################################################################
    248  # test function for this module: simply run the module as "main"
    249  # a text entry window will pop up. Write something, select arbitrary
    250  # segmnts of text with the mouse, and then right click the selection
    251  # for options like encryption, decryption, and setting of passphrase
    252  #############################################################################
    253  if __name__ == '__main__':
    254          app = wxPyWidgetTester(size = (400, 400))
    255          #show the login panel in a main window
    256          app.SetWidget(gmCryptoText, -1)
    257          app.MainLoop()
    258