GNOME Bugzilla – Bug 752392
gcr-viewer won't unlock p12 certificate with password length > 31 characters
Last modified: 2021-05-17 13:19:16 UTC
I am unable to import a p12 s/mime certificate using gcr if the certificate is encrypted with a password containing 32 or more characters. When trying to unlock the certificate, gcr responds as if the password entered was incorrect. Reported at: https://bugs.launchpad.net/ubuntu/+source/gcr/+bug/1400192 Thank you
Hi, Is this bug stil not fixed? I would like to work on it.
Hi Andrei, I don't know if it's fixed in upstream yet... Regards
Andrei, that would be great. No fix for this has landed in git yet.
Hi, I am not familiar with the gcr code, but I narrowed down some files where this bug might happen. I hope you can follow the wall text and give me some hints where this bug may happen because I still cannot figure it out ("-------->" is just a delimiter I've used to show the "flow" of the calls): --------> FILE: gcr/ui/gcr-viewer-widget.c: static gboolean on_parser_authenticate_for_unlock (GcrParser *parser, guint count, gpointer user_data) { GcrUnlockRenderer *unlock = GCR_UNLOCK_RENDERER (user_data); const gchar *password; if (count == 0) { password = _gcr_unlock_renderer_get_password (unlock); gcr_parser_add_password (parser, password); } return TRUE; } --------> in the same file, the above function is attached to a signal: static void on_unlock_renderer_clicked (GcrUnlockRenderer *unlock, gpointer user_data) { ......................... /* Override our main authenticate signal handler */ sig = g_signal_connect (self->pv->parser, "authenticate", G_CALLBACK (on_parser_authenticate_for_unlock), unlock); ......................... } --------> FILE: gcr/ui/gcr-unlock-renderer.c (I'm guessing this is the function called to retrieve the password from the entry box) const gchar * _gcr_unlock_renderer_get_password (GcrUnlockRenderer *self) { g_return_val_if_fail (GCR_IS_UNLOCK_RENDERER (self), NULL); return gtk_entry_get_text (self->pv->entry); } --------> FILE: gcr/gcr/gcr-parser.c (the docstring for this function says: /*Add a password to the set of passwords to try when parsing locked or encrypted items. This is usually called from the #GcrParser::authenticate signal. */) void gcr_parser_add_password (GcrParser *self, const gchar *password) { g_return_if_fail (GCR_IS_PARSER (self)); g_ptr_array_add (self->pv->passwords, egg_secure_strdup (password)); } --------> FILE: gcr/egg/egg-secure-memory.h static inline void* egg_secure_strdup (const char *str) { \ return egg_secure_strdup_full (G_STRINGIFY (tag), str, EGG_SECURE_USE_FALLBACK); \ --------> FILE: gcr/egg/egg-secure-memory.c char* egg_secure_strdup_full (const char *tag, const char *str, int options) { size_t len; char *res; if (!str) return NULL; len = strlen (str) + 1; res = (char *)egg_secure_alloc_full (tag, len, options); strcpy (res, str); return res; } --------> in the same file as above we have this function ("egg_secure_alloc_full") which I believe is the last to be called: void* egg_secure_alloc_full (const char *tag, size_t length, int flags) { Block *block; void *memory = NULL; if (tag == NULL) tag = "?"; if (length > 0xFFFFFFFF / 2) { if (egg_secure_warnings) fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", (unsigned long)length); return NULL; } /* Can't allocate zero bytes */ if (length == 0) return NULL; DO_LOCK (); for (block = all_blocks; block; block = block->next) { memory = sec_alloc (block, tag, length); if (memory) break; } /* None of the current blocks have space, allocate new */ if (!memory) { block = sec_block_create (length, tag); if (block) memory = sec_alloc (block, tag, length); } #ifdef WITH_VALGRIND if (memory != NULL) VALGRIND_MALLOCLIKE_BLOCK (memory, length, sizeof (void*), 1); #endif DO_UNLOCK (); if (!memory && (flags & EGG_SECURE_USE_FALLBACK) && EGG_SECURE_GLOBALS.fallback != NULL) { memory = EGG_SECURE_GLOBALS.fallback (NULL, length); if (memory) /* Our returned memory is always zeroed */ memset (memory, 0, length); } if (!memory) errno = ENOMEM; return memory; }
Do you have a reproducer? I would suggest building a test case (see test-parser.c) for parsing this such a file with a long password. Then it'll be easier to track why and where it fails.
No I don't have, but I reproduced the error myself by manual typying it (:/). I am going to do that now , thanks.
Created attachment 325417 [details] [review] test case It seems that gcr/gcr-parser.c around line 940 fails as it returns NULL: asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-8-EncryptedPrivateKeyInfo", data);
There seems to be a limit in generate_pkcs12 in egg/egg-symkey.c Andrei found the verify_pkcs12_safe to return GCR_ERROR_LOCKED. Drilling further down, generate_pkcs12 seems to be the culprit. /* Bring in the password, as 16bits per character BMP string, ie: UCS2 */ if (utf8_password) { p2 = utf8_password; for (i = 0; i < 64; i += 2) { It clearly only regards 32 characters of the password. If the password is shorter than 32 characters, the password is repeated, it seems. So it seems as if the password should indeed be normalised to 32 characters. I don't know what to do with longer passwords though. I guess the PKCS#12 spec has some answers...
Created attachment 326008 [details] [review] patch for PCKS#12 files with long password This patch does not yet solve the problem, but it adheres to the current way GnuTLS API is generating keys from password strings (as shown here https://gitlab.com/gnutls/gnutls/blob/master/lib/x509/pkcs12_encr.c#L57). There might be something else that needs to be changed in the function I modified it (generate_pkcs12) but at the moment I am missing more information. I would appreciate any feedback for this work, as I've spent some time searching what was the actual problem. Thanks :-)
Created attachment 326410 [details] [review] patch for pkcs12 files with long password As Tobias said above, password with more than 31 characters are truncated, so rest of the chars aren't taking into account when the hash is generated. So I've increased the buffer size and the loop range is now formalized to this ((n_password/32)*64)+64 This works and unlocks the certificate, but somehow only "half" of it is displayed in the GUI ...
FWIW: gpgsm also cannot decode the p12 file. >gpgsm --import gcr/fixtures/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.p12 gpgsm: 1016 bytes of RC2 encrypted text gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-1' gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-15' gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-2' gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-3' gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-4' gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-5' gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-6' gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-7' gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-8' gpgsm: password too long gpgsm: decryption failed; trying charset 'ISO-8859-9' gpgsm: password too long gpgsm: decryption failed; trying charset 'KOI8-R' gpgsm: password too long gpgsm: decryption failed; trying charset 'IBM437' gpgsm: password too long gpgsm: decryption failed; trying charset 'IBM850' gpgsm: password too long gpgsm: decryption failed; trying charset 'EUC-JP' gpgsm: password too long gpgsm: decryption failed; trying charset 'BIG5' gpgsm: password too long gpgsm: encryptedData error at "outer.outer.seq", offset 2 gpgsm: possibly bad passphrase given gpgsm: error at "bag.encryptedData", offset 49 gpgsm: error parsing or decrypting the PKCS#12 file gpgsm: total number processed: 0 That's on the aaaa....p12 file I generated with openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout privateKey.key -out certificate.crt and then openssl pkcs12 -export -in certs/personal.crt -inkey keys/personal.key -certfile certs/ca.crt -name "My example certificate" -out personal.example.p12 So maybe this is a problem with the spec. What's the reference for both the current implement and the expected behaviour?
-- GitLab Migration Automatic Message -- This bug has been migrated to GNOME's GitLab instance and has been closed from further activity. You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/gcr/-/issues/75.