Skip to content

Commit e3130c5

Browse files
Implement KeeShare support for group synchronization
Add receiving-end support for KeeShare to enable secure sharing of password groups between databases. This feature allows automatic synchronization of shared groups when the database is opened. Key features: - Parse KeeShare reference metadata from group CustomData - Handle both raw .kdbx files and .share container format (zip) - RSA signature verification using SHA-256 for secure imports - Automatic merge using KeePassLib's native Synchronize method - Integration hook in Database.LoadData for seamless operation The implementation follows KeePassXC's KeeShare specification and provides the import functionality requested by users for sharing passwords across devices and users. Fixes #1161
1 parent 0ba4cec commit e3130c5

15 files changed

+1879
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
namespace keepass2android.KeeShare
5+
{
6+
/// <summary>
7+
/// Result of a user's trust decision for an untrusted signer
8+
/// </summary>
9+
public enum TrustDecision
10+
{
11+
/// <summary>Trust this signer permanently (add to trusted keys)</summary>
12+
TrustPermanently,
13+
/// <summary>Trust this signer for this session only</summary>
14+
TrustOnce,
15+
/// <summary>Reject this signer (do not import)</summary>
16+
Reject,
17+
/// <summary>User cancelled the dialog</summary>
18+
Cancel
19+
}
20+
21+
/// <summary>
22+
/// Information about an untrusted signer presented to the user
23+
/// </summary>
24+
public class UntrustedSignerInfo
25+
{
26+
/// <summary>Name of the signer from the signature file</summary>
27+
public string SignerName { get; set; }
28+
29+
/// <summary>SHA-256 fingerprint of the public key (hex, lowercase)</summary>
30+
public string KeyFingerprint { get; set; }
31+
32+
/// <summary>Path to the share file</summary>
33+
public string SharePath { get; set; }
34+
35+
/// <summary>
36+
/// Get a formatted fingerprint for display (e.g., "AB:CD:EF:12:...")
37+
/// </summary>
38+
public string FormattedFingerprint
39+
{
40+
get
41+
{
42+
if (string.IsNullOrEmpty(KeyFingerprint) || KeyFingerprint.Length < 2)
43+
return KeyFingerprint;
44+
45+
// Format as colon-separated pairs for readability
46+
var result = new System.Text.StringBuilder();
47+
for (int i = 0; i < KeyFingerprint.Length; i += 2)
48+
{
49+
if (i > 0) result.Append(':');
50+
result.Append(KeyFingerprint.Substring(i, Math.Min(2, KeyFingerprint.Length - i)).ToUpperInvariant());
51+
}
52+
return result.ToString();
53+
}
54+
}
55+
}
56+
57+
/// <summary>
58+
/// Interface for handling user prompts during KeeShare import.
59+
/// Implement this in the Android UI layer to show dialogs to the user.
60+
/// </summary>
61+
public interface IKeeShareUserInteraction
62+
{
63+
/// <summary>
64+
/// Prompt the user to trust an unknown signer.
65+
/// Called when a share file is signed by a key not in the trusted store.
66+
/// </summary>
67+
/// <param name="signerInfo">Information about the untrusted signer</param>
68+
/// <returns>User's trust decision</returns>
69+
Task<TrustDecision> PromptTrustDecisionAsync(UntrustedSignerInfo signerInfo);
70+
71+
/// <summary>
72+
/// Notify the user that imports were completed.
73+
/// Called after CheckAndImport finishes processing all shares.
74+
/// </summary>
75+
/// <param name="results">List of import results</param>
76+
void NotifyImportResults(System.Collections.Generic.List<KeeShareImportResult> results);
77+
78+
/// <summary>
79+
/// Check if auto-import is enabled in user preferences.
80+
/// If false, shares will not be imported automatically on database load.
81+
/// </summary>
82+
bool IsAutoImportEnabled { get; }
83+
}
84+
85+
/// <summary>
86+
/// Default implementation that rejects all untrusted signers (no UI).
87+
/// Use this as a fallback when no UI handler is registered.
88+
/// </summary>
89+
public class DefaultKeeShareUserInteraction : IKeeShareUserInteraction
90+
{
91+
public Task<TrustDecision> PromptTrustDecisionAsync(UntrustedSignerInfo signerInfo)
92+
{
93+
// No UI available - reject by default for security
94+
Kp2aLog.Log($"KeeShare: No UI handler registered. Rejecting untrusted signer '{signerInfo?.SignerName}'");
95+
return Task.FromResult(TrustDecision.Reject);
96+
}
97+
98+
public void NotifyImportResults(System.Collections.Generic.List<KeeShareImportResult> results)
99+
{
100+
// No UI - just log
101+
if (results == null) return;
102+
foreach (var result in results)
103+
{
104+
if (result.IsSuccess)
105+
Kp2aLog.Log($"KeeShare: Imported {result.EntriesImported} entries from {result.SharePath}");
106+
}
107+
}
108+
109+
public bool IsAutoImportEnabled => true; // Default to enabled for backward compatibility
110+
}
111+
}

0 commit comments

Comments
 (0)