Repository: borgbackup/borg

Issue:
roundtrip the passphrase to the user
there was recently a borg user who had severe troubles with getting access to an encrypted repository.

besides other issues, the real blocker was that the passphrase did not work.

and it was maybe not just a "I forgot my passphrase" case (there is no help for that), but there were potentially other things influencing the correct reproduction of the passphrase:
- the original repo and archive creation was done on native OS X
- the original passphrase was maybe entered using a non-ascii / non-latin keyboard layout (user switched between multiple different keyboard layouts and was not completely sure about which one was active at the time when the passphrase was defined) - so the typed characters could be different.
- it was unclear whether there might have been a OS / locale misconfiguration on the original machine (and whether python's getpass() really resulted in the correct unicode string, whether it was decoded correctly from the bytestream received from OS / keyboard within python/getpass). single-byte encodings have the problem that they always work, potentially outputting garbage on decode if the wrong single-byte decoder is used.
- there was also the small possibility of (repeated) typos
- the user was able to access/decrypt the repo on the original machine
- the original machine was gone, extraction was tried on another machine using linux

While we can't help users who forget their passphrase, we can maybe make sure that the user knows what was actually received by borg when it asked for a passphrase, so he can check whether that is correct (to avoid all these outside-of-borg issues mentioned above).

Ideas:
- ask user if he wants to show the entered password
- if so, show the entered password string
- if so and the password is not pure ascii, show the repr of the string

This would also help for simpler cases when users e.g. have capslock active.



Discussion:

Resolving patch:
diff --git a/borg/key.py b/borg/key.py
--- a/borg/key.py
+++ b/borg/key.py
@@ -7,7 +7,7 @@
 from hmac import HMAC, compare_digest
 from hashlib import sha256, pbkdf2_hmac
 
-from .helpers import IntegrityError, get_keys_dir, Error
+from .helpers import IntegrityError, get_keys_dir, Error, yes
 from .logger import create_logger
 logger = create_logger()
 
@@ -184,6 +184,23 @@ def env_passphrase(cls, default=None):
     def getpass(cls, prompt):
         return cls(getpass.getpass(prompt))
 
+    @classmethod
+    def verification(cls, passphrase):
+        if yes('Do you want your passphrase to be displayed for verification? [yN]: ',
+               env_var_override='BORG_DISPLAY_PASSPHRASE'):
+            print('Your passphrase (between double-quotes): "%s"' % passphrase,
+                  file=sys.stderr)
+            print('Make sure the passphrase displayed above is exactly what you wanted.',
+                  file=sys.stderr)
+            try:
+                passphrase.encode('ascii')
+            except UnicodeEncodeError:
+                print('Your passphrase (UTF-8 encoding in hex): %s' %
+                      hexlify(passphrase.encode('utf-8')).decode('ascii'),
+                      file=sys.stderr)
+                print('As you have a non-ASCII passphrase, it is recommended to keep the UTF-8 encoding in hex together with the passphrase at a safe place.',
+                      file=sys.stderr)
+
     @classmethod
     def new(cls, allow_empty=False):
         passphrase = cls.env_passphrase()
@@ -194,6 +211,7 @@ def new(cls, allow_empty=False):
             if allow_empty or passphrase:
                 passphrase2 = cls.getpass('Enter same passphrase again: ')
                 if passphrase == passphrase2:
+                    cls.verification(passphrase)
                     logger.info('Remember your passphrase. Your data will be inaccessible without it.')
                     return passphrase
                 else:


Test patch: