From c6a037b6b1502a7b02e42445d5e85af24ab3c6e9 Mon Sep 17 00:00:00 2001 From: TickoSpy <81111545+TickoSpy@users.noreply.github.com> Date: Fri, 10 Jul 2026 21:35:05 +0200 Subject: [PATCH] Fix #12: don't lock out encryption keys lacking keyEncipherment bit v1.3.4 (dc69662) gated CanKeyTransfer on an exact keyEncipherment bit and CanKeyAgree on an exact keyAgreement bit. Encryption smartcard certs that mark their key with dataEncipherment (or otherwise omit the exact bit) then failed CanEncryptCms/CanDecryptCms, so GetAllPivKeyPairs dropped the live card key. The unlock dialog fell back to the stale, un-refreshed authorized key from the EKF and showed it as "not connected", blocking database access. Gate encryption capability on any of keyEncipherment | dataEncipherment | keyAgreement and let the key algorithm (pubKeyInfo) pick transfer vs agreement. Pure signing certs stay excluded (PR #11 intent); certs without a key-usage extension keep all bits via DefaultIfEmpty. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Crypto/Windows/WindowsKeyPair.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/EpiSource.KeePass.Ekf/Crypto/Windows/WindowsKeyPair.cs b/EpiSource.KeePass.Ekf/Crypto/Windows/WindowsKeyPair.cs index e6301f4..fa0e94f 100644 --- a/EpiSource.KeePass.Ekf/Crypto/Windows/WindowsKeyPair.cs +++ b/EpiSource.KeePass.Ekf/Crypto/Windows/WindowsKeyPair.cs @@ -125,16 +125,30 @@ public bool CanEncryptCms { } } + // Key usage flags asserting the certificate may be used for CMS encryption/decryption. + // Besides the standard RSA keyEncipherment and (EC)DH keyAgreement bits, some smartcards + // (e.g. PIV cards) mark their encryption certificate with dataEncipherment instead of + // keyEncipherment. Gate on any of these bits and let the key algorithm (pubKeyInfo) decide + // between key transfer and key agreement - requiring an exact bit per primitive locks out + // otherwise valid encryption keys (see issue #12). Absent a key usage extension all bits are + // assumed set (see UpdateKeyUsageFlags), so unrestricted certificates keep working. + private const X509KeyUsageFlags encryptionKeyUsage = + X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyAgreement; + + private bool KeyUsageAllowsEncryption { + get { return (this.keyUsageFlags & encryptionKeyUsage) != X509KeyUsageFlags.None; } + } + public bool CanKeyAgree { get { - return (this.keyUsageFlags & X509KeyUsageFlags.KeyAgreement) == X509KeyUsageFlags.KeyAgreement + return this.KeyUsageAllowsEncryption && this.pubKeyInfo.CanKeyAgree && (this.privKeyInfo == null || this.privKeyInfo.CanKeyAgree); } } public bool CanKeyTransfer { get { - return (this.keyUsageFlags & X509KeyUsageFlags.KeyEncipherment) == X509KeyUsageFlags.KeyEncipherment + return this.KeyUsageAllowsEncryption && this.pubKeyInfo.CanKeyTransfer && (this.privKeyInfo == null || this.privKeyInfo.CanKeyTransfer); } }