znote 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
znote/client.py ADDED
@@ -0,0 +1,568 @@
1
+ import __main__
2
+ import impmagic
3
+ import threading
4
+
5
+
6
+ @impmagic.loader(
7
+ {'module': 'chardet'},
8
+ )
9
+ def decode(message):
10
+ if chardet.detect(message)['encoding']!=None:
11
+ return message.decode(chardet.detect(message)['encoding'])
12
+ else:
13
+ return message.decode()
14
+
15
+
16
+ class Client():
17
+ def __init__(self, conn):
18
+ threading.Thread.__init__(self)
19
+ self.connexion = conn
20
+ self.killed = False
21
+ self.well_come = False
22
+
23
+ @impmagic.loader(
24
+ {'module': 'Crypto.Cipher', 'submodule': ['PKCS1_OAEP']},
25
+ {'module': 'cryptography.hazmat.backends', 'submodule': ['default_backend']},
26
+ {'module': 'cryptography.hazmat.primitives.ciphers', 'submodule': ['Cipher', 'algorithms', 'modes']}
27
+ )
28
+ def decrypt_data(self, message):
29
+ iv = message[:16]
30
+ sym_key_encrypted = message[16:272] # Taille de la clé RSA (2048 bits) / 8
31
+ ciphertext = message[272:]
32
+
33
+ # Déchiffrement de la clé symétrique avec RSA
34
+ decryptor = PKCS1_OAEP.new(__main__.settings.key)
35
+ sym_key = decryptor.decrypt(sym_key_encrypted)
36
+
37
+ # Déchiffrement du fichier avec AES en mode CFB
38
+ cipher = Cipher(algorithms.AES(sym_key), modes.CFB(iv), backend=default_backend())
39
+ decryptor = cipher.decryptor()
40
+ plaintext = decryptor.update(ciphertext) + decryptor.finalize()
41
+
42
+ return plaintext
43
+
44
+
45
+ #Réception des données
46
+ @impmagic.loader(
47
+ {'module': 'select'}
48
+ )
49
+ def receive(self):
50
+ data = b""
51
+ while True:
52
+ #Pour éviter les exceptions avec le socket non bloquant
53
+ #ready_to_read, _, _ = select.select([self.connexion], [], [], 1)
54
+ #if ready_to_read:
55
+ chunk = self.connexion.recv(2048)
56
+
57
+ if chunk and len(chunk)<2048:
58
+ return data+chunk
59
+
60
+ if not chunk:
61
+ return data
62
+
63
+ data += chunk
64
+
65
+
66
+ @impmagic.loader(
67
+ {'module': 'os'},
68
+ {'module': 'core.gen', 'submodule': ['gen_password']},
69
+ {'module': 'Crypto.Cipher', 'submodule': ['PKCS1_OAEP']},
70
+ {'module': 'cryptography.hazmat.backends', 'submodule': ['default_backend']},
71
+ {'module': 'cryptography.hazmat.primitives.ciphers', 'submodule': ['Cipher', 'algorithms', 'modes']}
72
+ )
73
+ def send(self, message):
74
+ if not isinstance(message, bytes):
75
+ message = message.encode()
76
+
77
+ # Génération de la clé symétrique (AES) et de l'IV
78
+ #sym_key = os.urandom(32) # 256 bits
79
+ sym_key = gen_password(32) # 256 bits
80
+ iv = os.urandom(16) # IV de 128 bits pour AES
81
+
82
+ # Chiffrement du fichier avec AES en mode CFB
83
+ plaintext = message
84
+
85
+ cipher = Cipher(algorithms.AES(sym_key), modes.CFB(iv), backend=default_backend())
86
+ encryptor = cipher.encryptor()
87
+ ciphertext = encryptor.update(plaintext) + encryptor.finalize()
88
+
89
+ # Chiffrement de la clé symétrique avec RSA
90
+ encryptor = PKCS1_OAEP.new(__main__.settings.keyC)
91
+ sym_key_encrypted = encryptor.encrypt(sym_key)
92
+
93
+ # Sauvegarde du IV, de la clé symétrique chiffrée et du fichier chiffré
94
+ message_cipher = iv + sym_key_encrypted + ciphertext
95
+
96
+ self.connexion.send(message_cipher)
97
+
98
+
99
+ #Premier appel avec le serveur
100
+ @impmagic.loader(
101
+ {'module': 'hashlib'},
102
+ {'module': 'app.display', 'submodule': ['logs', 'notify']},
103
+ {'module': 'core.client.connect', 'submodule': ['encrypt', 'decrypt']},
104
+ {'module': 'Crypto.PublicKey', 'submodule': ['RSA']},
105
+ )
106
+ def welcome(self):
107
+ if __main__.settings.token:
108
+ payload = {'method': 'welcome', 'token': __main__.settings.token, 'serverhash': __main__.settings.serverhash, 'clienthash': __main__.settings.clienthash}
109
+ logs("Envoi du welcome au serveur", component="client")
110
+
111
+ else:
112
+ payload = {'method': 'sendKey', 'content': __main__.settings.PubKey.decode()}
113
+ logs("Envoi de la clé public au serveur", component="client")
114
+
115
+ self.connexion.send(encrypt(json.dumps(payload).encode()))
116
+
117
+ try:
118
+ message_recu = self.receive()
119
+
120
+ except:
121
+ if self.killed:
122
+ logs(f"Arrêt du thread demandé", component="client")
123
+ else:
124
+ logs(f"Erreur avec le client. Arret de la connexion", "error", component="client")
125
+
126
+ if message_recu!=b'':
127
+ msg = decrypt(decode(message_recu))
128
+ msg = json.loads(msg)
129
+
130
+ if msg['method']=="sendKey":
131
+ __main__.settings.PubServ = msg['content'].encode()
132
+ try:
133
+ #print("Connexion réussi")
134
+ __main__.settings.keyC = RSA.importKey(__main__.settings.PubServ)
135
+
136
+ #Enregistrement de la clé public du serveur
137
+ pubkey_path = os.path.join(__main__.settings.config_dir, "server.pub")
138
+ with open(pubkey_path, "wb") as f:
139
+ f.write(__main__.settings.PubServ)
140
+
141
+ #Calcul du hash de la clé du serveur
142
+ __main__.settings.serverhash = hashlib.sha256(__main__.settings.PubServ).hexdigest()
143
+
144
+ __main__.settings.pubserv_set = True
145
+
146
+ except Exception as err:
147
+ __main__.settings.PubServ=None
148
+ __main__.settings.pubserv_set = False
149
+
150
+ elif msg['method']=="welcome":
151
+ if 'message' in msg:
152
+ notify(msg['message'], "error")
153
+
154
+ if 'status' in msg:
155
+ if msg['status']==0:
156
+ __main__.settings.pubserv_set = False
157
+ #self.welcome()
158
+
159
+ elif msg['status']==2:
160
+ if os.path.exists(__main__.settings.token_file):
161
+ os.remove(__main__.settings.token_file)
162
+ exit()
163
+
164
+
165
+ @impmagic.loader(
166
+ {'module': '__main__'},
167
+ )
168
+ def get_note_content(self, note):
169
+ command = {
170
+ 'method': 'note',
171
+ 'action': 'get',
172
+ 'note': note,
173
+ 'token': __main__.settings.token
174
+ }
175
+
176
+ message = self.call_server(command)
177
+ if message['method']=="note":
178
+ if 'content' in message:
179
+ return message['content']
180
+
181
+ return ""
182
+
183
+
184
+ #Envoie une commande au serveur et attends une réponse
185
+ @impmagic.loader(
186
+ {'module': '__main__'},
187
+ {'module': 'app.display', 'submodule': ['logs']},
188
+ {'module': 'core.pack', 'submodule': ['pack', 'unpack']},
189
+ )
190
+ def call_server(self, command):
191
+ if not self.well_come:
192
+ self.welcome()
193
+ self.well_come = True
194
+
195
+ message = None
196
+
197
+ #if not __main__.settings.pubserv_set:
198
+ #if not __main__.settings.serverhash:
199
+
200
+ if not __main__.settings.pubserv_set:
201
+ return None
202
+ #exit()
203
+ #try:
204
+ self.send(pack(command, __main__.settings.key))
205
+
206
+ try:
207
+ message_recu = self.receive()
208
+
209
+ except:
210
+ if self.killed:
211
+ logs(f"Arrêt du thread demandé", component="client")
212
+ else:
213
+ logs(f"Erreur avec le client. Arret de la connexion", "error", component="client")
214
+ exit()
215
+
216
+ if message_recu!=b'':
217
+ message = self.decrypt_data(message_recu)
218
+ message = unpack(message, __main__.settings.keyC)
219
+
220
+ return message
221
+
222
+
223
+ @impmagic.loader(
224
+ {'module': '__main__'},
225
+ {'module': 'json'},
226
+ {'module': 'os'},
227
+ {'module': 'hashlib'},
228
+ {'module': 'core.editor', 'submodule': ['Editor']},
229
+ {'module': 'app.display', 'submodule': ['logs', 'notify', 'print_nxs', 'SaveScreen', 'RestoreScreen']},
230
+ {'module': 'core.pack', 'submodule': ['pack']},
231
+ {'module': 'zpp_color', 'submodule': ['fg', 'attr']},
232
+ {'module': 'app.input', 'submodule': ['getpass']},
233
+ {'module': 'zpp_ManagedFile', 'submodule': ['ManagedFile']}
234
+ )
235
+ def run(self, argument, parameter):
236
+ command = None
237
+
238
+ if argument!=None and len(parameter):
239
+ instruction = parameter[0]
240
+
241
+ if instruction=="login":
242
+ username = input("Username: ")
243
+ password = getpass("Password: ")
244
+
245
+ command = {
246
+ 'method': 'authentication',
247
+ 'username': username,
248
+ 'password': password
249
+ }
250
+
251
+ __main__.settings.token = None
252
+ if os.path.exists(__main__.settings.token_file):
253
+ os.remove(__main__.settings.token_file)
254
+
255
+ elif instruction=="status":
256
+ command = {
257
+ 'method': 'status',
258
+ 'token': __main__.settings.token
259
+ }
260
+
261
+ elif instruction=="logout":
262
+ command = {
263
+ 'method': 'logout',
264
+ 'token': __main__.settings.token
265
+ }
266
+
267
+ if os.path.exists(__main__.settings.token_file):
268
+ os.remove(__main__.settings.token_file)
269
+
270
+ elif instruction=="workspace":
271
+ command = {
272
+ 'method': 'workspace',
273
+ 'token': __main__.settings.token
274
+ }
275
+
276
+ if len(parameter)>1:
277
+ command['workspace'] = parameter[1]
278
+
279
+ if argument.create:
280
+ command['action'] = 'create'
281
+
282
+ elif argument.remove:
283
+ command['action'] = 'remove'
284
+
285
+ elif instruction=="add" or instruction=="edit":
286
+ if len(parameter)>1:
287
+ command = {
288
+ 'method': 'note',
289
+ 'action': instruction,
290
+ 'note': parameter[1],
291
+ 'token': __main__.settings.token
292
+ }
293
+
294
+ try:
295
+ file = ManagedFile(mode='a', typefile='stringio')
296
+
297
+ if instruction=="edit":
298
+ file.write(self.get_note_content(parameter[1]))
299
+ file.seek(0)
300
+
301
+ SaveScreen()
302
+ editor = Editor(filename=parameter[1], file=file)
303
+ editor.app.run()
304
+ RestoreScreen()
305
+ file.seek(0)
306
+
307
+ command['content'] = file.read()
308
+
309
+ except Exception as e:
310
+ command = None
311
+ logs(f"An unexpected error occurred: {e}", "error")
312
+
313
+ elif instruction=="view" or instruction=="pull":
314
+ if len(parameter)>1:
315
+ content_note = self.get_note_content(parameter[1])
316
+
317
+ if instruction=="view":
318
+ print(content_note)
319
+
320
+ elif instruction=="pull":
321
+ if len(parameter)>2:
322
+ filename = parameter[2]
323
+
324
+ if os.path.exists(filename):
325
+ notify(f"Le fichier {filename} existe déjà", "error")
326
+
327
+ with open(filename, "w") as f:
328
+ f.write(content_note)
329
+
330
+ elif instruction=="push":
331
+ if len(parameter)>1:
332
+ filename = parameter[1]
333
+
334
+ if os.path.exists(filename):
335
+ with open(filename, "r") as f:
336
+ content_note = f.read()
337
+
338
+ if len(parameter)>2:
339
+ note_name = parameter[2]
340
+ else:
341
+ note_name = os.path.basename(filename)
342
+
343
+
344
+ command = {
345
+ 'method': 'note',
346
+ 'action': "add",
347
+ 'note': note_name,
348
+ 'content': content_note,
349
+ 'token': __main__.settings.token
350
+ }
351
+
352
+ elif instruction=="remove":
353
+ if len(parameter)>1:
354
+ command = {
355
+ 'method': 'note',
356
+ 'action': "remove",
357
+ 'note': parameter[1],
358
+ 'token': __main__.settings.token
359
+ }
360
+
361
+ elif instruction=="list":
362
+ command = {
363
+ 'method': 'note',
364
+ 'action': 'list',
365
+ 'note': '',
366
+ 'token': __main__.settings.token
367
+ }
368
+
369
+ elif instruction=="info":
370
+ if len(parameter)>1:
371
+ command = {
372
+ 'method': 'note',
373
+ 'action': 'info',
374
+ 'note': parameter[1],
375
+ 'token': __main__.settings.token
376
+ }
377
+
378
+ elif instruction=="find":
379
+ if len(parameter)>1:
380
+ command = {
381
+ 'method': 'note',
382
+ 'action': 'find',
383
+ 'pattern': parameter[1],
384
+ 'token': __main__.settings.token
385
+ }
386
+
387
+ elif instruction=="protect":
388
+ if len(parameter)>1:
389
+ password = getpass("Password: ")
390
+ note_password = hashlib.sha256(password.encode()).hexdigest()
391
+
392
+ command = {
393
+ 'method': 'note',
394
+ 'action': 'protect',
395
+ 'note': parameter[1],
396
+ 'password_hash': note_password,
397
+ 'token': __main__.settings.token
398
+ }
399
+
400
+ if command:
401
+ self.send_and_response(command)
402
+
403
+
404
+ @impmagic.loader(
405
+ {'module': '__main__'},
406
+ {'module': 'app.display', 'submodule': ['logs', 'notify', 'print_nxs']},
407
+ {'module': 'core.pack', 'submodule': ['pack']},
408
+ {'module': 'core.client.token', 'submodule': ['set_token']},
409
+ {'module': 'app.input', 'submodule': ['getpass']},
410
+ )
411
+ def send_and_response(self, command):
412
+ message = self.call_server(command)
413
+
414
+ if message['method']=="setToken":
415
+ if 'content' in message:
416
+ __main__.settings.token = message['content']
417
+ set_token(__main__.settings.token)
418
+ logs(f"Reception du token", component="client")
419
+ notify("Connecté", "success")
420
+ else:
421
+ logs("Token invalide", "error")
422
+
423
+ elif message['method']=="workspace":
424
+ if 'content' in message:
425
+ for element in message['content']:
426
+ if 'active' in message and element==message['active']:
427
+ print(f"* {element}")
428
+ else:
429
+ print(f" {element}")
430
+
431
+ elif message['method']=="status":
432
+ for element in ['connected', 'workspace', 'notes']:
433
+ if element in message:
434
+ print_nxs(f"{element}: ", color='dark_gray',nojump=True)
435
+
436
+ if isinstance(message[element], bool):
437
+ if message[element]==True:
438
+ print_nxs(message[element], color="green")
439
+ else:
440
+ print_nxs(message[element], color="light_red")
441
+
442
+ else:
443
+ print_nxs(message[element])
444
+
445
+ elif message['method']=="list":
446
+ if 'notes' in message:
447
+ for note in message['notes']:
448
+ print(f" - {note}")
449
+
450
+ elif message['method']=="info":
451
+ if 'note' in message:
452
+ for key, value in message['note'].items():
453
+ print_nxs(f"{key}: ", color='dark_gray',nojump=True)
454
+ print_nxs(value)
455
+
456
+ elif message['method']=="find":
457
+ if 'content' in message:
458
+ for note in message['content']:
459
+ print(f" - {note}")
460
+
461
+
462
+ elif message['method']=="notify":
463
+ if 'message' in message and 'status_code' in message:
464
+ notify(message['message'], message['status_code'])
465
+
466
+ elif message['method']=="password_required" and 'payload' in message:
467
+ password = getpass("Password: ")
468
+ payload = message['payload']
469
+
470
+ payload['password_hash'] = hashlib.sha256(password.encode()).hexdigest()
471
+
472
+ self.send_and_response(payload)
473
+
474
+ else:
475
+ print(message)
476
+
477
+ self.send(pack({'method': 'endSession'}, __main__.settings.key))
478
+ """
479
+ except Exception as err:
480
+ logs(err, "error", component="client")
481
+
482
+ finally:
483
+ self.connexion.close()
484
+ """
485
+
486
+
487
+ #Récupération de l'id du thread en cours
488
+ def get_id(self):
489
+ if hasattr(self, '_thread_id'):
490
+ return self._thread_id
491
+ for id, thread in threading._active.items():
492
+ if thread is self:
493
+ return id
494
+
495
+
496
+ #Permet l'arrêt du thread en cours
497
+ @impmagic.loader(
498
+ {'module': 'ctypes'},
499
+ {'module': 'app.display', 'submodule': ['logs']},
500
+ )
501
+ def kill_thread(self):
502
+ logs("Requête d'arrêt reçue", "info")
503
+ self.killed = True
504
+ thread_id = self.get_id()
505
+ res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
506
+ ctypes.py_object(SystemExit))
507
+ if res > 1:
508
+ ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
509
+ #print('Exception raise failure')
510
+ self.connexion.close()
511
+ #th_E.kill_thread()
512
+
513
+
514
+ @impmagic.loader(
515
+ {'module': '__main__'},
516
+ {'module': 'zpp_args'},
517
+ {'module': 'app.display', 'submodule': ['logs']},
518
+ {'module': 'core.client.settings', 'submodule': ['Settings']},
519
+ {'module': 'core.client.connect', 'submodule': ['connect_server']},
520
+ )
521
+ def run_client():
522
+ parse = zpp_args.parser()
523
+ parse.set_description("znote")
524
+ parse.set_argument(longname="config", description="Spécifier le chemin du fichier de config", store="value", default=None)
525
+ parse.set_argument(longname="init", description="Initialiser les paramètres de l'application", default=None)
526
+ parse.set_argument(longname="create", description="Création d'un workspace", default=False, category="workspace")
527
+ parse.set_argument(longname="remove", description="Suppression d'un workspace", default=False, category="workspace")
528
+ parse.set_argument(longname="info", description="Afficher les informations de connexion", default=False)
529
+ #parse.set_parameter("command", description="commande à lancer")
530
+ parse.set_parameter("login", "Connexion à un serveur de note")
531
+ parse.set_parameter("logout", "Déconnexion d'un serveur de note")
532
+ parse.set_parameter("status", "Affichage du statut de l'application")
533
+ parse.set_parameter("workspace", "Création/Suppression/Changement de workspace")
534
+ parse.set_parameter("add", "Ajouter une note")
535
+ parse.set_parameter("edit", "Editer une note")
536
+ parse.set_parameter("view", "Voir une note")
537
+ parse.set_parameter("pull", "Récupérer une note")
538
+ parse.set_parameter("push", "Envoyer une note")
539
+ parse.set_parameter("remove", "Supprimer une note")
540
+ parse.set_parameter("list", "Lister les notes")
541
+ parse.set_parameter("info", "Afficher les informations d'une note")
542
+ parse.set_parameter("find", "Rechercher un contenu dans les notes")
543
+ parse.set_parameter("protect", "Protéger une note par mot de passe")
544
+ parse.disable_check()
545
+ parameter, argument = parse.load()
546
+
547
+ if argument!=None:
548
+ __main__.settings = Settings(argument.config)
549
+
550
+ if argument.init:
551
+ __main__.settings.init_settings()
552
+
553
+ elif argument.info:
554
+ logs(f"{__main__.settings.host}:{__main__.settings.port}")
555
+
556
+ else:
557
+ __main__.settings.load_key()
558
+ connexion = connect_server()
559
+
560
+ if connexion==0:
561
+ logs("Impossible d'accéder au serveur", "error")
562
+ else:
563
+ __main__.th_R = Client(connexion)
564
+ __main__.th_R.run(argument, parameter)
565
+
566
+
567
+ if __name__ == "__main__":
568
+ run_client()
znote/core/auth.py ADDED
@@ -0,0 +1,71 @@
1
+ import __main__
2
+ import impmagic
3
+
4
+
5
+ @impmagic.loader(
6
+ {'module': '__main__'},
7
+ {'module': 'base64'},
8
+ {'module': 'secrets'}
9
+ )
10
+ def get_token(username):
11
+ length = 32
12
+ # Génère un token binaire aléatoire
13
+ token_bytes = secrets.token_bytes(length)
14
+ # Encode le token en base64 pour qu'il soit lisible et transmissible
15
+ token = base64.urlsafe_b64encode(token_bytes).rstrip(b'=').decode('utf-8')
16
+
17
+ user_data = {
18
+ 'username': username,
19
+ }
20
+
21
+ __main__.server.USER[token] = user_data
22
+
23
+ return token
24
+
25
+
26
+ @impmagic.loader(
27
+ {'module': 'hashlib', 'submodule': ['sha256']}
28
+ )
29
+ def hash_password(password):
30
+ return sha256(password.encode("utf-8")).hexdigest()
31
+
32
+
33
+ @impmagic.loader(
34
+ {'module': '__main__'},
35
+ {'module': 'pony.orm', 'submodule': ['db_session']}
36
+ )
37
+ def authenticate(username, password):
38
+ with db_session:
39
+ user = __main__.settings.orm.User.get(username=username)
40
+ if user and user.password_hash == hash_password(password):
41
+ return True
42
+ return False
43
+
44
+
45
+ @impmagic.loader(
46
+ {'module': '__main__'},
47
+ {'module': 'pony.orm', 'submodule': ['db_session']}
48
+ )
49
+ def get_userid(username):
50
+ with db_session:
51
+ user = __main__.settings.orm.User.get(username=username)
52
+ if user:
53
+ return user.id
54
+ return None
55
+
56
+
57
+ #Création d'un nouvel utilisateur
58
+ @impmagic.loader(
59
+ {'module': '__main__'},
60
+ {'module': 'datetime', 'submodule': ['datetime']},
61
+ {'module': 'pony.orm', 'submodule': ['db_session', 'commit']}
62
+ )
63
+ def create_user(username, password):
64
+ with db_session:
65
+ if not __main__.settings.orm.User.get(username=username):
66
+ __main__.settings.orm.User(username=username, password_hash=hash_password(password), created_at=datetime.utcnow())
67
+ commit()
68
+
69
+ return ""
70
+ else:
71
+ return "User already exist"
@@ -0,0 +1,52 @@
1
+ import __main__
2
+ import impmagic
3
+
4
+
5
+ @impmagic.loader(
6
+ {'module': 'socket'},
7
+ {'module': 'app.display', 'submodule': ['logs']},
8
+ )
9
+ def connect_server():
10
+ connexion = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
11
+ try:
12
+ connexion.connect((__main__.settings.host, __main__.settings.port))
13
+ except socket.error:
14
+ return 0
15
+ logs("Connexion établie avec le serveur.", "success")
16
+ return connexion
17
+
18
+
19
+ @impmagic.loader(
20
+ {'module': 'base64','submodule': ['b64encode']},
21
+ {'module': 'Crypto.Cipher','submodule': ['AES']},
22
+ {'module': 'Crypto.Hash','submodule': ['SHA256']},
23
+ {'module': 'Crypto','submodule': ['Random']}
24
+ )
25
+ def encrypt(source, encode=True):
26
+ key = __main__.settings.totp.now()
27
+ key = SHA256.new(key.encode()).digest()
28
+ IV = Random.new().read(AES.block_size)
29
+ encryptor = AES.new(key, AES.MODE_CBC, IV)
30
+ padding = AES.block_size - len(source) % AES.block_size
31
+ source += bytes([padding]) * padding
32
+ data = IV + encryptor.encrypt(source)
33
+ return b64encode(data) if encode else data
34
+
35
+
36
+ @impmagic.loader(
37
+ {'module': 'base64','submodule': ['b64decode']},
38
+ {'module': 'Crypto.Cipher','submodule': ['AES']},
39
+ {'module': 'Crypto.Hash','submodule': ['SHA256']}
40
+ )
41
+ def decrypt(source, decode=True):
42
+ key = __main__.settings.totp.now()
43
+ if decode:
44
+ source = b64decode(source)
45
+ key = SHA256.new(key.encode()).digest()
46
+ IV = source[:AES.block_size]
47
+ decryptor = AES.new(key, AES.MODE_CBC, IV)
48
+ data = decryptor.decrypt(source[AES.block_size:])
49
+ padding = data[-1]
50
+ if data[-padding:] != bytes([padding]) * padding:
51
+ raise ValueError("Invalid padding...")
52
+ return data[:-padding].decode("Utf8")