1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 try:
38 import wxversion
39 import wx
40 except ImportError:
41 from wxPython import wx
42
43 import string, rotor, binascii
44
45 ID_POP_ENCRYPT = wx.NewId()
46 ID_POP_DECRYPT = wx.NewId()
47 ID_POP_PASSPHRASE = wx.NewId()
48
49 -class gmTextctrlFileDropTarget(wx.FileDropTarget):
50 """ a generic text control widget that accepts dropped files """
51
52 - def __init__(self, textwindow):
53 wx.FileDropTarget.__init__(self)
54 self.textwindow=textwindow
55
56 - def OnDropFiles(self, x, y, filenames):
57 """inserts the dropped file(s) content(s) at the cursor position"""
58 for file in filenames:
59 self.textwindow.WriteText(string.join(open(file, 'r').readlines()))
60
61
62 -class gmCryptoText(wx.TextCtrl):
63 """A special text widget that supports cryptography
64
65 A right mouse click pops up a manu that allows to encrypt
66 or decrypt selected text segments.
67 You can drag and drop any number of text files into the text
68 widget, and that text will be inserted at the current cursor
69 position
70 """
71
72 - def __init__(self, parent, id, size=wx.DefaultSize, style=wx.TE_MULTILINE|wx.TE_RICH, defaulttext=None):
73
74 wx.TextCtrl.__init__(self, parent, id, size=size, style=style)
75 self.SetDefaultStyle(wx.TextAttr(wx.RED))
76
77
78 self.fuzzymargin = 25
79 self.passphrase = None
80
81 self. passphrase_expiry = 120
82
83 self.textselection = None
84 self. selectionStart = 0
85 self.selectionEnd = 0
86
87 if defaulttext is not None:
88 self.WriteText(defaulttext)
89
90
91 self.aID = wx.NewId()
92
93
94 dt = gmTextctrlFileDropTarget(self)
95 self.SetDropTarget(dt)
96
97
98 wx.EVT_RIGHT_UP(self,self.OnRightClick)
99 wx.EVT_RIGHT_DOWN(self, self.OnRightDown)
100
101
102
103
104 - def OnRightClick(self, event):
105 "A right mouse click triggers a popup menu for cryptographic functionality"
106
107 self.selectionStart, self.selectionEnd = self.GetSelection()
108
109
110 menu = wx.Menu()
111 menu.Append(ID_POP_ENCRYPT, _("Encrypt"))
112 menu.Append(ID_POP_DECRYPT, _("Decrypt"))
113 menu.Append(ID_POP_PASSPHRASE, _("Set pass phrase"))
114
115
116 wx.EVT_MENU(self, ID_POP_ENCRYPT, self.OnEncrypt)
117 wx.EVT_MENU(self, ID_POP_DECRYPT, self.OnDecrypt)
118 wx.EVT_MENU(self, ID_POP_PASSPHRASE, self.OnSetPassphrase)
119
120
121 self.PopupMenu(menu, wxPoint(event.GetX(), event.GetY()))
122
123
124 menu.Destroy()
125
126
127 event.Skip()
128
129
130 def OnContextMenu(self, event):
131 pass
132
133
134 - def OnEncrypt(self, event):
135 """triggered by popup contect menu event"""
136
137
138 self.textselection = self.GetValue()[self.selectionStart:self.selectionEnd]
139
140 if len(self.textselection)<1:
141 return
142
143
144 if self.passphrase is None:
145 self.passphrase = self.AskForPassphrase()
146 if self.passphrase == None:
147 return
148
149
150
151 self.Replace(self.selectionStart, self.selectionEnd, \
152 '<!' + self.GetIdentTag() + binascii.hexlify(self.Encrypt(self.textselection, self.passphrase)) + '!>')
153
154
155 - def OnDecrypt(self, event):
156
157 if self.passphrase is None:
158 self.passphrase = self.AskForPassphrase()
159 if self.passphrase == None:
160 return
161
162 textselection, self.selectionStart, self.selectionEnd = \
163 self.FuzzyScanSelection(self.selectionStart, self.selectionEnd, self.fuzzymargin)
164
165 if textselection[:2] != '<!' or textselection[-2:] != '!>':
166 wx.MessageBox(_("This is not correctly encrypted text!"))
167 return
168
169 textselection = textselection[2:-2]
170 identtag, textselection = self.StripIdentTag(textselection)
171
172
173 decoded = self.Decrypt(binascii.unhexlify(textselection), self.passphrase, identtag)
174 self.Replace(self.selectionStart, self.selectionEnd, decoded)
175
176
177 - def OnSetPassphrase(self, event):
178 self.passphrase = self.AskForPassphrase()
179
180
181 - def OnRightDown(self, event):
182 """dummy function; if this event was not intercepted, GTK would
183 clear the text selection the very moment the mouse button is clicked"""
184 pass
185
187 """asks for a pass phrase and returns it"""
188 dlg = wxTextEntryDialog(self, _("Please enter your pass phrase:"), _("Pass phrase expired"), style=wxOK|wxCANCEL|wx.CENTRE|wx.TE_PASSWORD)
189 if dlg.ShowModal() == wx.ID_OK:
190 retval = dlg.GetValue()
191 else:
192 retval = None
193 dlg.Destroy()
194 return retval
195
196
197 - def Encrypt(self, cleartext, key):
198 """override this function for your own crypto funcs"""
199 rt = rotor.newrotor(key, 12)
200 return rt.encrypt(cleartext)
201
202
203 - def Decrypt(self, ciphertext, key, identtag):
204 """override this function for your own crypto funcs"""
205 rt = rotor.newrotor(key, 12)
206 return rt.decrypt(ciphertext)
207
208
209 - def StripIdentTag(self, text):
210 """Remove the 'ident tag' from text and return both tag and test"""
211 if text[0] != '[':
212 "No ident tag ?"
213 return '', text
214 try:
215 endtag = string.index(text, ']')+1
216 except ValueError:
217 return '', text
218 return text[:endtag], text[endtag:]
219
220
221 - def GetIdentTag(self):
222 """This is a 'virtual' function which should be overridden to provide your own meaningful tag"""
223 return '[rotor]'
224
225
226 - def SetFuzzyMargin(self, margin):
227 """The fuzzy margin is the number of characters on each side of the text selection
228 the decryption algorithm will search for correct delimiters. It should be at least as long as
229 the IdentTag is plus an extra 3 characters to allow for the crypto tag"""
230 self.fuzzymargin = margin
231
232
233 - def FuzzyScanSelection(self, frompos, topos, margin):
234 fulltext = self.GetValue()
235
236 start = frompos - margin
237 if start < 0: start = 0
238 if frompos == 0: frompos = 1
239
240 finish = topos + margin
241 if finish > len(fulltext): finish = len(fulltext)
242 if topos > len(fulltext)-2: topos = len (fulltext)-2
243 try:
244 left = string.rindex(fulltext, '<', start, frompos)
245 right = string.index(fulltext, '>', topos, finish)+1
246 except ValueError:
247 wx.LogMessage("FuzzyScan went wrong")
248 return ''
249 return fulltext[left:right], left,right
250
251
252
253
254
255
256
257 if __name__ == '__main__':
258 _ = lambda x:x
259 app = wxPyWidgetTester(size = (400, 400))
260
261 app.SetWidget(gmCryptoText, -1)
262 app.MainLoop()
263