Advertisement
Guest User

Untitled

a guest
Apr 15th, 2019
142
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.42 KB | None | 0 0
  1. import sqlite3
  2. import hashlib
  3. import base64
  4. from tkinter import *
  5.  
  6. from cryptography.fernet import Fernet
  7.  
  8. DB_FILE_NAME = 'my.db'
  9.  
  10.  
  11.  
  12. class AuthenticationError(ValueError):
  13. pass
  14.  
  15.  
  16. class EncryptedDB():
  17.  
  18. class ToEncrypt(bytes):
  19. '''
  20. A wrapper class around bytes for passing to sqlite.
  21. '''
  22.  
  23. @staticmethod
  24. def bytes_check(s, encoding='UTF-8'):
  25. if isinstance(s, str):
  26. s = bytes(s, encoding)
  27. if not isinstance(s, bytes):
  28. raise ValueError(
  29. 'must be bytes or str, not {}'.format(type(s))
  30. )
  31. return s
  32. @staticmethod
  33. def derive_password(p):
  34. p = EncryptedDB.bytes_check(p)
  35. return hashlib.sha256(p).digest()
  36.  
  37.  
  38.  
  39. def __init__(self):
  40. self.db_file = DB_FILE_NAME
  41. self.conn = sqlite3.connect(
  42. self.db_file
  43. ,detect_types=sqlite3.PARSE_DECLTYPES)
  44. self.conn.row_factory = sqlite3.Row
  45. self.conn.executescript('''
  46. CREATE TABLE IF NOT EXISTS version_info (
  47. version INTEGER
  48. );
  49. CREATE TABLE IF NOT EXISTS encryptedstring (
  50. id INTEGER PRIMARY KEY AUTOINCREMENT
  51. ,owner TEXT
  52. ,data ENCRYPTED
  53. ,description TEXT
  54. ,FOREIGN KEY(owner) REFERENCES login(username)
  55. );
  56. CREATE TABLE IF NOT EXISTS login (
  57. username TEXT PRIMARY KEY
  58. ,password SHA256
  59. ,salt BLOB
  60. ,userkey ENCRYPTED
  61. );
  62. ''')
  63.  
  64. self.fernet = None
  65. sqlite3.register_converter('SHA256', EncryptedDB.derive_password)
  66.  
  67.  
  68. def requires_login(f):
  69. def wrap(self, *args, **kwargs):
  70. if hasattr(self, 'logged_in_user'):
  71. r = f(self, *args, **kwargs)
  72. else:
  73. raise AuthenticationError(
  74. 'This method requires a prior call to login.'
  75. )
  76. return r
  77. return wrap
  78.  
  79.  
  80. def make_login(self, username, password):
  81. self.conn.execute('''
  82. insert into login(username, password) values(?,?)
  83. ''', (username, password))
  84. self.save()
  85.  
  86. def login(self, user, password):
  87. r = self.conn.execute('''
  88. select * from login
  89. where username = ? and password = ?
  90. ''', (user, password)).fetchone()
  91. if r is None:
  92. raise AuthenticationError(
  93. 'No such credential combination exists'
  94. )
  95.  
  96. password = EncryptedDB.derive_password(password)
  97. password = base64.urlsafe_b64encode(password)
  98. self.fernet = Fernet(password)
  99. sqlite3.register_converter('ENCRYPTED', self.fernet.decrypt)
  100. sqlite3.register_adapter(EncryptedDB.ToEncrypt, self.fernet.encrypt)
  101. self.logged_in_user = user
  102.  
  103.  
  104.  
  105. @requires_login
  106. def insert(self, data, description=None):
  107. data = EncryptedDB.bytes_check(data)
  108. data = EncryptedDB.ToEncrypt(data)
  109. if len(description) == 0:
  110. description = None
  111. self.conn.execute('''
  112. insert into encryptedstring(owner, data, description) values(?,?,?)
  113. ''', (self.logged_in_user, data, description) )
  114.  
  115. @requires_login
  116. def get(self, *rowids):
  117. return self.conn.execute('''
  118. select * from encryptedstring
  119. where id in ({})
  120. '''.format(
  121. ','.join('?'*len(rowids))
  122. )
  123. , rowids).fetchall()
  124.  
  125.  
  126. @requires_login
  127. def list(self):
  128. return self.conn.execute('''
  129. select id, description
  130. from encryptedstring
  131. where owner = ?
  132. order by id
  133. ''', (self.logged_in_user,)).fetchall()
  134.  
  135. def save(self):
  136. try:
  137. self.conn.execute('commit')
  138. return True
  139. except sqlite3.OperationalError:
  140. pass
  141. return False
  142.  
  143. def __str__(self):
  144. if not hasattr(self, 'logged_in_user'):
  145. return repr(self)
  146. largest_id = self.conn.execute('''
  147. select id
  148. from encryptedstring
  149.  
  150. order by id desc
  151. ''').fetchone()
  152. if largest_id is None:
  153. return ''
  154. largest_id = largest_id['id']
  155. fmt = f'{{:{largest_id}}}: {{}}\r'
  156. s = ''
  157. for r in self.list():
  158. s += fmt.format(*r)
  159. return s
  160.  
  161.  
  162. class App(Tk):
  163. def __init__(self, *args, **kwargs):
  164. super().__init__(*args, **kwargs)
  165. self.db = EncryptedDB()
  166. self.render_login()
  167.  
  168. def render_login(self):
  169. self.num_failed_logins = 0
  170. f = Frame(self)
  171. self.nentry = Entry(f, width=20)
  172. self.pentry = Entry(f, width=20, show=b'\xe2\x80\xa2'.decode())
  173. self.errorlabel = Label(self, fg='red')
  174. self.new_user = Button(self, text='New Login', command=self.add_creds)
  175.  
  176. f.pack()
  177. self.nentry.grid(row=0,column=1)
  178. self.pentry.grid(row=1,column=1)
  179. Label(f, text='Username').grid(row=0,column=0)
  180. Label(f, text='Password').grid(row=1,column=0)
  181. Button(self, text='Submit', command=self.verify_creds).pack(anchor='center')
  182. Button(self, text='New Login', command=self.add_creds).pack(side=RIGHT,anchor='e')
  183. self.errorlabel.pack()
  184.  
  185. def add_creds(self):
  186. u = self.nentry.get()
  187. p = self.pentry.get()
  188. if len(u) == 0 or len(p) == 0:
  189. self.errorlabel.config(text='cant be blank')
  190. return
  191.  
  192. try:
  193. self.db.make_login(u, p)
  194. except sqlite3.IntegrityError:
  195. self.errorlabel.config(text='username taken')
  196. return
  197.  
  198. self.verify_creds()
  199.  
  200.  
  201. def verify_creds(self):
  202. u = self.nentry.get()
  203. p = self.pentry.get()
  204. try:
  205. self.db.login(u,p)
  206. except AuthenticationError:
  207. if self.num_failed_logins == 0:
  208. self.errorlabel.config(text='invalid credentials')
  209. self.num_failed_logins += 1
  210. return
  211.  
  212. # login correct, do whatever
  213. [w.pack_forget() for w in self.winfo_children()]
  214. self.render_main()
  215.  
  216. def render_main(self):
  217. self.geometry('800x600')
  218. self.main_frame = Frame(self)
  219. self.main_frame.pack(expand=True, fill=BOTH)
  220. self.update_main_display()
  221. Button(self, text='New Entry', command=self.insert_popup).pack()
  222.  
  223. def insert_popup(self):
  224. def do_insert():
  225. self.db.insert(
  226. data.get(1.0, END)
  227. ,description=desc.get()
  228. )
  229. self.db.save()
  230. self.update_main_display()
  231. w.destroy()
  232.  
  233.  
  234. w = Toplevel(self)
  235. f = Frame(w)
  236. f.pack()
  237. Label(f, text='Description:').pack()
  238. desc = Entry(f)
  239. desc.pack(expand=True, fill=X)
  240. Label(f, text='Secret Text:').pack()
  241. data = Text(f)
  242. data.pack(expand=True, fill=BOTH)
  243. Button(f, text='Submit', command=do_insert).pack()
  244.  
  245.  
  246. def update_main_display(self):
  247. for w in self.main_frame.winfo_children():
  248. w.pack_forget()
  249. for i, row in enumerate(self.db.list()):
  250. d = row['description']
  251. if d is None:
  252. d = '< No description >'
  253. Label(self.main_frame, text=str(i+1)).grid(row=i, column=0)
  254. l = Label(self.main_frame, text=d)
  255. l.id = row['id']
  256. l.bind('<Button-1>', lambda cb: self.show_secret(l.id))
  257. l.grid(row=i, column=1)
  258.  
  259. def show_secret(self, id):
  260. s = self.db.get(id)
  261. # get returns a list but we are just getting 1, so extract from list
  262. if len(s) == 1:
  263. s = s[0]['data'].decode()
  264. else:
  265. #this should never happen
  266. s = '< error: this should never happen >'
  267. raise RuntimeError(
  268. 'record id from update_main_display returned '
  269. 'more than one record. uh oh :)'
  270. )
  271.  
  272. if s is not None:
  273. w = Toplevel(self)
  274. t = Text(w)
  275. t.insert(0.0, s)
  276. t.config(state=DISABLED)
  277. t.pack()
  278.  
  279.  
  280.  
  281. if __name__ == '__main__':
  282. App().mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement