From fcbd5999f28f287507844b55f2bee82e714620c8 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 3 Nov 2026 13:57:05 +0100 Subject: fix(app): harden the setup key imports - PGP key import reads at most 1 MiB (bounded read, buffer zeroed) and refuses larger files with a new message. - SshKeyViewModel catches any Exception from import and generation (CancellationException rethrown) instead of a short list, so an unexpected provider or sshj error no longer crashes the app. - Pending PGP key bytes are zeroed in a finally block, also on cancellation during addKey after a replace, and an earlier pending pick is zeroed before a new one takes its place. - PGPKeyManager never replaces a stored secret key with a public-only key of the same ID, with or without replace (KeyDowngradeException); the setup ViewModel shows a localized message for it. Unit test added. - storeRecipients strips '#' comments and a trailing '!' as pass reads .gpg-id; tests added. - GitServerViewModel.clearStore returns the failure when the store folder cannot be emptied, so the clone does not start; the plan's Task 3 call site reports it. - readGpgId opens the file with NOFOLLOW_LINKS and reads at most 64 KiB + 1 bytes, refusing more. - SshKey.import writes the new private key to a temp file and moves it over atomically before dropping the old key's Keystore entry, public key and passphrase, so an I/O error keeps the old key. - KeyUtils.isSecretKey notes (ponytail) that GnuPG stub and smartcard keyrings count as secret. Co-Authored-By: Claude Opus 5.5 --- .../main/kotlin/app/passwordstore/crypto/errors/CryptoException.kt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'crypto/common/src/main') diff --git a/crypto/common/src/main/kotlin/app/passwordstore/crypto/errors/CryptoException.kt b/crypto/common/src/main/kotlin/app/passwordstore/crypto/errors/CryptoException.kt index af6145d0..9589ceec 100644 --- a/crypto/common/src/main/kotlin/app/passwordstore/crypto/errors/CryptoException.kt +++ b/crypto/common/src/main/kotlin/app/passwordstore/crypto/errors/CryptoException.kt @@ -39,6 +39,10 @@ public class KeyNotFoundException(keyId: String) : public class KeyAlreadyExistsException(keyId: String) : KeyManagerException("Pre-existing key was found for $keyId") +/** Attempting to replace a stored secret key with a public-only key of the same `keyId`. */ +public class KeyDowngradeException(keyId: String) : + KeyManagerException("A secret key is stored for $keyId; a public key cannot replace it") + /** Sealed exception types for [app.passwordstore.crypto.CryptoHandler]. */ public sealed class CryptoHandlerException(message: String? = null, cause: Throwable? = null) : CryptoException(message, cause) -- cgit v1.2.3