Robert Leftwich avatar Robert Leftwich committed a3f5b33

Handle unicode only passwords on os x. Tighten up regex used for password matching return value of security call on os x and ensure unicode chars are unicode-only. Fixes #85

Comments (0)

Files changed (3)


 class Keyring(KeyringBackend):
     """Mac OS X Keychain"""
+    # regex for extracting password from security call
+    password_regex = re.compile("""password:\s*(?:0x(?P<hex>[0-9A-F]+)\s*)?"""
+                                """(?:"(?P<pw>.*)")?""")
     def supported(self):
         """Recommended for all OSX environment.
             if output == 'password: \n':
                 return ''
             # search for special password pattern.
-            matches ='password:(?P<hex>.*?)"(?P<pw>.*)"', output)
+            matches =
             if matches:
-                hex ='hex').strip()
-                pw ='pw')
+                group_dict = matches.groupdict()
+                hex = group_dict.get('hex')
+                pw = group_dict.get('pw')
                 if hex:
                     # it's a weird hex password, decode it.
-                    return binascii.unhexlify(hex[2:])
+                    return unicode(binascii.unhexlify(hex), 'utf-8')
                     # it's a normal password, send it back.
                     return pw


     def init_keyring(self):
         return OS_X.Keyring()
+    @unittest.expectedFailure
+    def test_delete_present(self):
+        """Not implemented"""
+        super(OSXKeychainTestCase, self).test_delete_present()


 from keyring import errors
 DIFFICULT_CHARS = string.whitespace + string.punctuation
-UNICODE_CHARS = escape.u("""κόσμεНа берегу пустынных волнSîne klâwen durh die
-wolken sint geslagen, er stîget ûf mit grôzer kraft""")
+# unicode only characters
+# Sourced from The Quick Brown Fox... Pangrams
+UNICODE_CHARS = escape.u(
+    """זהכיףסתםלשמועאיךתנצחקרפדעץטובבגן"""
+    """ξεσκεπάζωτηνψυχοφθόραβδελυγμία"""
+    """Съешьжеещёэтихмягкихфранцузскихбулокдавыпейчаю"""
+    """Жълтатадюлябешещастливачепухъткойтоцъфназамръзнакатогьон"""
+# ensure no-ascii chars slip by - watch your editor!
+assert min(ord(char) for char in UNICODE_CHARS) > 127
 class BackendBasicTests(object):
-    """Test for the keyring's basic funtions. password_set and password_get
+    """Test for the keyring's basic functions. password_set and password_get
     def setUp(self):
         service = random_string(20, UNICODE_CHARS)
         self.check_set_get(service, username, password)
+    def test_unicode_and_ascii_chars(self):
+        source = (random_string(10, UNICODE_CHARS) + random_string(10) +
+                 random_string(10, DIFFICULT_CHARS))
+        password = random_string(20, source)
+        username = random_string(20, source)
+        service = random_string(20, source)
+        self.check_set_get(service, username, password)
     def test_different_user(self):
         Issue #47 reports that WinVault isn't storing passwords for
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.