Compare commits

..

1 commit

Author SHA1 Message Date
Lysann Tranvouez
e33df1f5cc do not search too far for .gpg-id + lots of tests for that 2026-03-13 21:37:55 +01:00
2 changed files with 20 additions and 19 deletions

View file

@ -445,7 +445,7 @@ public class PasswordStore {
// Walk up the directory hierarchy, but never escape the store root // Walk up the directory hierarchy, but never escape the store root
let storeRoot = storeURL.absoluteURL.resolvingSymlinksInPath() let storeRoot = storeURL.absoluteURL.resolvingSymlinksInPath()
var current = storeRoot.appendingPathComponent(relativePath).resolvingSymlinksInPath() var current = storeRoot.appendingPathComponent(relativePath).resolvingSymlinksInPath()
while current.path.hasPrefix(storeRoot.path) { while current.path.hasPrefix(storeRoot.path) {
let candidate = current.appendingPathComponent(".gpg-id") let candidate = current.appendingPathComponent(".gpg-id")
var isDir: ObjCBool = false var isDir: ObjCBool = false

View file

@ -30,7 +30,7 @@ The codebase does **not** support encrypting to multiple public keys. Every laye
### 1. `findGPGID(from:) -> [String]` ### 1. `findGPGID(from:) -> [String]`
Split file contents by newline, trim each line, filter empty lines. Return array of key IDs. Split file contents by newline, trim each line, filter empty lines. Return array of key IDs. Callers that only need a single key (e.g. for decryption routing) can use `.first`.
### 2. `PGPInterface` protocol ### 2. `PGPInterface` protocol
@ -60,10 +60,10 @@ When a store lists multiple key IDs in `.gpg-id`, the user needs the public keys
### Current state ### Current state
- The keychain holds **one** `pgpPublicKey` blob and **one** `pgpPrivateKey` blob, but each can contain **multiple concatenated armored key blocks**. Both interface implementations parse all keys from these blobs. - The keychain holds exactly **one** `pgpPublicKey` blob and **one** `pgpPrivateKey` blob.
- The import UI (armor paste, URL, file picker) has one public key field + one private key field. Importing **replaces** the set of keys entirely, there is no append mode for adding additional keys or managing existing keys. - The import UI (armor paste, URL, file picker) has one public key field + one private key field. Importing **replaces** the previous key pair entirely.
- There is no UI for viewing which key IDs are loaded or for importing additional recipient-only public keys, nor for viewing the key metadata. - Both `GopenPGPInterface` and `ObjectivePGPInterface` *can* parse multiple keys from a single armored blob (e.g. concatenated armor blocks). So if the user pastes multiple public keys into the single field, they would be parsed — but the encrypt path only uses one key, and the UI doesn't communicate this.
- There is no UI for viewing or editing `.gpg-id` files, which are the source of truth for which keys are used for encryption. - There is no UI for viewing which key IDs are loaded.
### Key storage approach ### Key storage approach
@ -142,19 +142,20 @@ This can be expensive for large directories. Show progress and allow cancellatio
## Implementation Order ## Implementation Order
| Step | Description | Status | Depends On | | Step | Description | Depends On |
|------|-------------|--------|------------| |------|-------------|------------|
| 1 | `findGPGIDs` returns `[String]` + update callers | ✅ Done | — | | 1 | `findGPGID` returns `[String]` + update callers | — |
| 2 | `PGPInterface` protocol change (`keyIDs: [String]`) | ✅ Done | — | | 2 | `PGPInterface` protocol change (`keyIDs: [String]?`) | — |
| 3 | `GopenPGPInterface` multi-key encryption | ✅ Done | Step 2 | | 3 | `GopenPGPInterface` multi-key encryption | Step 2 |
| 4 | `ObjectivePGPInterface` multi-key encryption | ✅ Done | Step 2 | | 4 | `ObjectivePGPInterface` multi-key encryption | Step 2 |
| 5 | `PGPAgent` updated overloads | ✅ Done | Steps 2-4 | | 5 | `PGPAgent` updated overloads | Steps 2-4 |
| 6 | `PasswordStore.encrypt()` uses `[String]` from `findGPGIDs` | ✅ Done | Steps 1+5 | | 6 | `PasswordStore.encrypt()` uses `[String]` from `findGPGID` | Steps 1+5 |
| 7 | UI: import additional recipient public keys | Not started | Step 5 | | 7 | UI: import additional recipient public keys | Step 5 |
| 8 | UI: view loaded key IDs and metadata | Not started | Step 5 | | 8 | UI: view loaded key IDs | Step 5 |
| 9a | UI: view `.gpg-id` in password detail / folder view | Not started | Step 1 | | 9a | UI: view `.gpg-id` in password detail / folder view | Step 1 |
| 9b | UI: edit `.gpg-id` | Not started | Step 9a | | 9b | UI: edit `.gpg-id` | Step 9a |
| 10 | Re-encryption when `.gpg-id` changes | Not started | Steps 6+9b | | 10 | Re-encryption when `.gpg-id` changes | Steps 6+9b |
| T | Tests (see testing section) | Steps 1-10 |
--- ---