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 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
70 wx.TextCtrl.__init__(self, parent, id, size=size, style=style)
71 self.SetDefaultStyle(wx.TextAttr(wx.RED))
72
73
74 self.fuzzymargin = 25
75 self.passphrase = None
76
77 self. passphrase_expiry = 120
78
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
87 self.aID = wx.NewId()
88
89
90 dt = gmTextctrlFileDropTarget(self)
91 self.SetDropTarget(dt)
92
93
94 wx.EVT_RIGHT_UP(self,self.OnRightClick)
95 wx.EVT_RIGHT_DOWN(self, self.OnRightDown)
96
97
98
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
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
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
117 self.PopupMenu(menu, wxPoint(event.GetX(), event.GetY()))
118
119
120 menu.Destroy()
121
122
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
134 self.textselection = self.GetValue()[self.selectionStart:self.selectionEnd]
135
136 if len(self.textselection)<1:
137 return
138
139
140 if self.passphrase is None:
141 self.passphrase = self.AskForPassphrase()
142 if self.passphrase == None:
143 return
144
145
146
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
161 if textselection[:2] != '<!' or textselection[-2:] != '!>':
162 wx.MessageBox(_("This is not correctly encrypted text!"))
163 return
164
165 textselection = textselection[2:-2]
166 identtag, textselection = self.StripIdentTag(textselection)
167
168
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
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.Destroy()
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
232 start = frompos - margin
233 if start < 0: start = 0
234 if frompos == 0: frompos = 1
235
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
249
250
251
252
253 if __name__ == '__main__':
254 _ = lambda x:x
255 app = wxPyWidgetTester(size = (400, 400))
256
257 app.SetWidget(gmCryptoText, -1)
258 app.MainLoop()
259