-
-
Notifications
You must be signed in to change notification settings - Fork 159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support longer passwords in Windows #540
Comments
Yes, I'd welcome such a change. In addition to devising a protocol for splitting and restoring the password, you'll also need a scheme for naming such passwords... and you'll notice that the Windows Keyring already has a hack to support multiple passwords for the same system with different users, so you'll want to be careful these two naming schemes don't interact badly. You'll also want to consider compatibility - what happens when a user stores a long password and then downgrades keyring... or stores a short one and then upgrades keyring? The upgrade path is more important to consider than the downgrade path. |
I hope to have time this weekend to work on this. I will pay attention to the multiple passwords / different users issue. Also, I realized I need to pay attention to For the upgrade keyring case, I was thinking that the first "shard" would maintain the The main breakage I can picture is if one upgraded, stored a password bigger than 1280 (i.e. more than one shard), and then downgraded, older keyring wouldn't know about the subsequent shards and would return a 1280 byte broken password, silently, without any indication that something was wrong. |
@jaraco I tried submitting a Pull Request, but I don't see where it went. I'm inexperienced with If you can't see my Pull Request, perhaps provide some tips so that I can send it in properly. |
@jaraco, I did some reading and thinking about my sharding strategy, and I have a concern. First off, as background, I wanted to understand why we see the apparent max password size of 1280 characters. As an older keyring issue 355 asserted, the Microsoft doc of the "CREDENTIAL" structure says that
However, when I inspect the //
// Maximum size of the CredBlob field (in bytes)
//
#define CRED_MAX_CREDENTIAL_BLOB_SIZE (5*512)
DWORD CredentialBlobSize;
_Field_size_bytes_(CredentialBlobSize) LPBYTE CredentialBlob; Since 5 * 512 = 2560 bytes, that's room for 1280 2-byte >>> ffi.sizeof('wchar_t')
2 That's the background for why the byte limit is 2560 bytes. My concern is that a single unicode character does not have to convert to a single >>> import emoji
>>> s = emoji.emojize(':thumbs_up:')
>>> s
'👍'
>>> len(s)
1
>>> import cffi
>>> ffi = cffi.FFI()
>>> ffi.new('wchar_t[]', s)
<cdata 'wchar_t[]' owning 6 bytes> but (not including the NULL added by The sharding, to be done correctly, must be done on the encoded byte count, not on the character count. In other words, the real limit is 2560 bytes, not 1280 characters. The obstacle for readily sharding on the byte count is that the API we are using fromdict does the conversion to bytes for us. Of course for things like JSON Web Tokens (which is the use-case that gave me the issue to begin with), the text in question is actually BASE64 text, and so the ASCII characters "by luck/design" all map one-to-one to a single Alternatively, lower-level API that let us encode in Please let me know what you think. |
@jaraco, on a separate topic from the sharding...a comment in Windows.py says
The way I understood that comment, I expected that the check for service name collision would also check for a different user name, but in set_password, the code only checks for service name collision via def set_password(self, service, username, password):
existing_pw = self._get_password(service)
if existing_pw:
# resave the existing password using a compound target
existing_username = existing_pw['UserName']
target = self._compound_name(existing_username, service)
self._set_password(
target,
existing_username,
existing_pw.value,
)
self._set_password(service, username, str(password)) and doesn't additionally check if Since get_password first checks for the "non-compound service name", I am thinking the code is nevertheless correct. But since it doesn't seem to align 100% with the comment, I wanted to check in about this. By way of an example >>> import keyring
>>> keyring.set_password('a_svc', 'a_user', 'aaaa')
>>> keyring.set_password('a_svc', 'a_user', 'bbbb') produces these two entries in Windows Credentials Manager: |
@jaraco I submitted a new pull request #544. It addresses the Partifact issue that was blocking me. It has a cleaner approach to sharding passwords across multiple entries, and support UTF-8 encoding in, I think, a sustainable way. I apologize in advance for my lack of knowledge about setuptools, packaging, etc. Let me know what you think! |
@jaraco I know this issue is closed, but the Windows limit is getting in our way, too, and I have a possible idea for how to fix it.
Here's the version of Windows I'm on
The limit documented by @cvubrugier is the same limit I am experiencing: 1280 max for the password.
Yet some packages use
keyring
to store tokens (JWT) inkeyring
, and these tokens can easily exceed 1280 bytes.So my idea is to edit
https://github.com/jaraco/keyring/blob/main/keyring/backends/Windows.py
, in particular_set_password
and_get_password
, notice the "too big" password, and split it up into as many pieces as necessary, giving each one a unique (and numbered)TargetName
.Since
_set_password
and_get_password
are the callers ofwin32.CredWrite
andwin32.CredRead
, I can transparently split the password up in_set_password
and then transparently re-join it up in_get_password
and no other code will be the wiser for it.I've prototyped this and it works.
Is this a PR you would potentially welcome (or, minimally, indulge?)
Originally posted by @jrobbins-LiveData in #355 (comment)
The text was updated successfully, but these errors were encountered: