Skip to content

Commit 3ad090a

Browse files
committed
Merge branch 'feature/cryptography' into develop
2 parents 3c72189 + b0ca8fa commit 3ad090a

10 files changed

Lines changed: 495 additions & 132 deletions

File tree

README.rst

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
PyAuth
22
======
33

4+
.. image:: https://img.shields.io/pypi/v/PyAuth.png
5+
:target: https://pypi.python.org/pypi/PyAuth/
6+
:alt: Latest Version
7+
8+
.. image:: https://img.shields.io/pypi/pyversions/PyAuth.png
9+
:target: https://pypi.python.org/pypi/PyAuth/
10+
:alt: Latest Version
11+
12+
.. image:: https://img.shields.io/github/release/tknarr/PyAuth.png
13+
:target: https://github.com/tknarr/PyAuth/releases/latest
14+
:alt: Latest Version
15+
416
Copyright 2016 Todd T Knarr <tknarr@silverglass.org>
517

618
This program is free software: you can redistribute it and/or modify it under
719
the terms of the GNU General Public License as published by the Free Software
820
Foundation, either version 3 of the License, or (at your option) any later
9-
version.
21+
version. The Fernet AES256 implementation (fernet256.py) is dual licensed
22+
under the terms of the Apache License version 2.0 and the BSD License as
23+
noted in the source file.
1024

1125
This program is distributed in the hope that it will be useful, but WITHOUT
1226
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
@@ -24,10 +38,10 @@ and other software and hardware using the standard TOTP algorithm outlined in
2438
`RFC 6238 <https://tools.ietf.org/html/rfc6238>`_ (support for the HOTP algorithm
2539
outlined in `RFC 4226 <https://tools.ietf.org/html/rfc4226>`_ is planned).
2640

27-
Secrets are encrypted in the database using AES encryption in CBC mode. There is
28-
no option for storing unencrypted secrets. If you were using an older beta version
29-
of this program, you will be prompted for a password and upon first save the
30-
secrets will be encrypted using it.
41+
Secrets are encrypted using AES256, there is no option for storing unencrypted
42+
secrets. If you were using an older beta version, you will be prompted for a
43+
password and the stored secrets will be migrated to the current encryption without
44+
requiring any more user intervention.
3145

3246
PyPI page: `https://pypi.python.org/pypi/PyAuth <https://pypi.python.org/pypi/PyAuth>`_
3347

@@ -38,7 +52,9 @@ Prerequisites
3852
* `wxPython <http://www.wxpython.org/>`_ 3.0 or higher, which requires matching
3953
`wxWidgets <http://www.wxwidgets.org/>`_
4054
* `pyotp 2.0.1 <https://pypi.python.org/pypi/pyotp>`_ or higher
41-
* `pycrypto 2.6.1 <https://pypi.python.org/pypi/pycrypto>`_ or higher
55+
* `cryptography 1.3 <https://pypi.python.org/pypi/cryptography>`_ or higher
56+
* `pycrypto 2.6.1 <https://pypi/python.org/pypi/pycrypto>`_ or higher, strictly for
57+
decrypting older databases
4258

4359
wxPython isn't automatically pulled in by ``pip`` because the version at PyPI is
4460
still 2.9. Your distribution probably includes a pre-packaged version, or you can

VERSIONS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# PyAuth version history:
22

3-
* 1.0.0 - Initial release
3+
* 0.9.10 - Implement authenticated encryption
44

55
* 0.9.7 - Keyboard accelerators working
66

pyauth/About.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"""Metadata about the program."""
33

44
import sysconfig
5-
import base64
65
import io
76
import pkg_resources
87
import wx

pyauth/AuthFrame.py

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ChangeDatabasePasswordDialog import ChangeDatabasePasswordDialog
1717
from HTMLTextDialog import HTMLTextDialog
1818
from Logging import GetLogger
19+
from Errors import DecryptionError, PasswordError
1920

2021
class AuthTaskbarIcon( wx.TaskBarIcon ):
2122
"""Notification tray icon."""
@@ -205,13 +206,22 @@ def OnCreate( self, event ):
205206
break
206207
try:
207208
self.auth_store = AuthenticationStore( Configuration.GetDatabaseFilename(), password )
208-
except ValueError:
209+
except DecryptionError as e:
210+
GetLogger().error( str( e ) )
211+
GetLogger().error( "Failure decrypting database." )
209212
retry = True
210-
else:
213+
except PasswordError as e:
214+
GetLogger().error( str( e ) )
215+
GetLogger().error( "Password problem decrypting database." )
216+
retry = True
217+
except Exception as e:
218+
GetLogger().error( str( e ) )
219+
GetLogger().critical( "Unexpected exception decrypting database." )
211220
retry = False
212221
finally:
213222
if self.auth_store != None:
214223
authentication_store_ok = True
224+
retry = False
215225
if not authentication_store_ok:
216226
GetLogger().critical( "Database could not be opened" )
217227
self.do_not_save = True
@@ -427,7 +437,14 @@ def OnCloseWindow( self, event ):
427437
self.timer = None
428438
if not self.do_not_save:
429439
if self.auth_store != None:
430-
self.auth_store.Save()
440+
try:
441+
self.auth_store.Save()
442+
except PasswordError:
443+
dlg = wx.MessageDialog( self, "A database password is required.", "Error",
444+
style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP | wx.CENTRE )
445+
dlg.SetExtendedMessage( "The database could not be properly saved." )
446+
dlg.ShowModal()
447+
dlg.Destroy()
431448
wp = self.GetPosition()
432449
Configuration.SetLastWindowPosition( wp )
433450
if self.displayed:
@@ -503,7 +520,12 @@ def OnMenuNewEntry( self, event ):
503520
GetLogger().debug( "AF NE account %s", account )
504521
GetLogger().debug( "AF NE digits %d", digits )
505522
GetLogger().debug( "AF NE orig lbl %s", original_label )
506-
entry = self.auth_store.Add( provider, account, secret, digits, original_label )
523+
try:
524+
entry = self.auth_store.Add( provider, account, secret, digits, original_label )
525+
sts = ''
526+
except PasswordError:
527+
entry = None
528+
sts = 'password'
507529
if entry != None:
508530
GetLogger().debug( "AF NE new panel: %d", entry.GetGroup() )
509531
# If all we have is the dummy entry then replace it, otherwise add the new entry at the end
@@ -524,10 +546,15 @@ def OnMenuNewEntry( self, event ):
524546
## unicode( panel.GetMinSize() ) )
525547
self.UpdatePanelSize()
526548
else:
527-
GetLogger().debug( "AF NE duplicate item" )
528-
dlg = wx.MessageDialog( self, "That entry already exists.", "Error",
529-
style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP | wx.CENTRE )
530-
dlg.SetExtendedMessage( "Provider: {0}\nAccount: {1}".format( provider, account ) )
549+
if sts == 'password':
550+
GetLogger().debug( "AF NE password error" )
551+
dlg = wx.MessageDialog( self, "A database password is required.", "Error",
552+
style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP | wx.CENTRE )
553+
else:
554+
GetLogger().debug( "AF NE duplicate item" )
555+
dlg = wx.MessageDialog( self, "That entry already exists.", "Error",
556+
style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP | wx.CENTRE )
557+
dlg.SetExtendedMessage( "Provider: {0}\nAccount: {1}".format( provider, account ) )
531558
dlg.ShowModal()
532559
dlg.Destroy()
533560

@@ -568,10 +595,14 @@ def OnMenuEditEntry( self, event ):
568595
GetLogger().debug( "AF UE updating entry" )
569596
status = self.auth_store.Update( entry.GetGroup(), provider, account, secret, digits )
570597
if status < 0:
571-
dlg = wx.MessageDialog( self, "Database is corrupted.", "Error",
572-
style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP | wx.CENTRE )
573-
dlg.SetExtendedMessage( "Multiple copies of the entry were found.\n" +
574-
"The database is likely corrupted and needs repaired." )
598+
if status == -100:
599+
dlg = wx.MessageDialog( self, "A database password is required.", "Error",
600+
style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP | wx.CENTRE )
601+
else:
602+
dlg = wx.MessageDialog( self, "Database is corrupted.", "Error",
603+
style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP | wx.CENTRE )
604+
dlg.SetExtendedMessage( "Multiple copies of the entry were found.\n" +
605+
"The database is likely corrupted and needs repaired." )
575606
dlg.ShowModal()
576607
dlg.Destroy()
577608
elif status == 0:
@@ -779,7 +810,13 @@ def OnMenuReindex( self, event ):
779810
"""Handle a request to reindex the database from the menu."""
780811
GetLogger().debug( "AF menu Reindex command" )
781812
GetLogger().info( "Database reindex ordered" )
782-
self.auth_store.Reindex()
813+
try:
814+
self.auth_store.Reindex()
815+
except PasswordError:
816+
dlg = wx.MessageDialog( self, "A database password is required.", "Error",
817+
style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP | wx.CENTRE )
818+
dlg.ShowModal()
819+
dlg.Destroy()
783820
self.depopulate_entries_window()
784821
self.populate_entries_window()
785822
self.UpdatePanelSize()
@@ -788,7 +825,13 @@ def OnMenuRegroup( self, event ):
788825
"""Handle a request to re-group the database from the menu."""
789826
GetLogger().debug( "AF menu Regroup command" )
790827
GetLogger().info( "Database regroup and reindex ordered" )
791-
self.auth_store.Regroup()
828+
try:
829+
self.auth_store.Regroup()
830+
except PasswordError:
831+
dlg = wx.MessageDialog( self, "A database password is required.", "Error",
832+
style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP | wx.CENTRE )
833+
dlg.ShowModal()
834+
dlg.Destroy()
792835
self.depopulate_entries_window()
793836
self.populate_entries_window()
794837
self.UpdatePanelSize()

0 commit comments

Comments
 (0)