This is a C# implementation of a local stored password manager in .Net 10. The application provides a secure way to store and manage passwords locally on the user's device.
- Password Storage: Store accounts and services passwords securely
- History log: Log every events
- Trigger warnings: Trigger warnings when detected
- Autosave: Autosave updates
- Password Generation: Generate strong, unique passwords
- Encryption: All passwords are encrypted using AES with a set of keys and RSA with a 1024-bit key
- Access Control: Access to the password store is restricted to authorized users only
Models
classDiagram
direction LR
%% Main Interfaces
namespace Upsilon.Apps.Passkey.Interfaces.Utils {
class ISerializationCenter {
<<interface>>
+Serialize~T~(in toSerialize T) string
+Deserialize~T~(in toDeserialize string) T
}
class IClipboardManager {
<<interface>>
+RemoveAllOccurence(in removeList IEnumerable~string~) int
}
class IPasswordFactory {
<<interface>>
+Alphabetic : string
+Numeric : string
+SpecialChars : string
+GeneratePassword(in length int, in alphabet string, in checkIfLeaked bool) string
+PasswordLeaked(in password string) bool
}
class ICryptographyCenter {
<<interface>>
+HashLength : int
+GetHash(in source string) string
+GetSlowHash(in source string) string
+Sign(inout source string) void
+CheckSign(inout source string) bool
+EncryptSymmetrically(inout source string, in passwords IEnumerable~string~) string
+DecryptSymmetrically(inout source string, in passwords IEnumerable~string~) string
+GenerateRandomKeys(out publicKey string, out privateKey string) void
+EncryptAsymmetrically(inout source string, in key string) string
+DecryptAsymmetrically(inout source string, in key string) string
}
}
namespace Upsilon.Apps.Passkey.Interfaces.Models {
class IItem {
<<interface>>
+ItemId : string
+Database : IDatabase
}
class IAccount {
<<interface>>
+Service : IService
+Label : string
+Notes : string
+Identifiers : IEnumerable~string~
+Password : string
+Passwords : IDictionary~DateTime, string~
+PasswordUpdateReminderDelay : int
+Options : AccountOption
}
class IService {
<<interface>>
+User : IUser
+ServiceName : string
+Url : string
+Notes : string
+Accounts : IEnumerable~IAccount~
+AddAccount(in label string, in identifiers IEnumerable~string~, in password string) IAccount
+AddAccount(in label string, in identifiers IEnumerable~string~) IAccount
+AddAccount(in identifiers IEnumerable~string~, in password string) IAccount
+AddAccount(in identifiers IEnumerable~string~) IAccount
+DeleteAccount(in account IAccount) void
}
class IUser {
<<interface>>
+Username : string
+Passkeys : IEnumerable~string~
+LogoutTimeout : int
+CleaningClipboardTimeout : int
+ShowPasswordDelay : int
+NumberOfOldPasswordToKeep : int
+WarningsToNotify : WarningType
+Services : IEnumerable~IService~
+AddService(in serviceName string) IService
+DeleteService(in service IService) void
}
class IDatabase {
<<interface>>
+DatabaseFile : string
+User : IUser
+SessionLeftTime : int
+Logs : IEnumerable~ILog~
+Warnings : IEnumerable~IWarning~
+SerializationCenter : ISerializationCenter
+CryptographyCenter : ICryptographyCenter
+PasswordFactory : IPasswordFactory
+ClipboardManager : IClipboardManager
+WarningDetected : EventHandler~WarningDetectedEventArgs~
+AutoSaveDetected : EventHandler~AutoSaveDetectedEventArgs~
+DatabaseSaved : EventHandler
+DatabaseClosed : EventHandler~LogoutEventArgs~
+Login(in passkey string) IUser
+Save(void) void
+Delete(void) void
+Close(void) void
+HasChanged(void) bool
+HasChanged(in itemId string) bool
+HasChanged(in itemId string, in fieldName string) bool
+ImportFromFile(in filePath string) bool
+ExportToFile(in filePath string) bool
}
class ILog {
<<interface>>
+DateTime : DateTime
+Message : string
+NeedsReview : bool
}
class IWarning {
<<interface>>
+WarningType : WarningType
+Logs : IEnumerable~ILog~
+Accounts : IEnumerable~IAccount~
}
}
%% Enums
namespace Upsilon.Apps.Passkey.Interfaces.Enums {
class AccountOption {
<<enumeration>>
None
WarnIfPasswordLeaked
}
class WarningType {
<<enumeration>>
LogReviewWarning
PasswordUpdateReminderWarning
DuplicatedPasswordsWarning
PasswordLeakedWarning
}
class AutoSaveMergeBehavior {
<<enumeration>>
MergeAndSaveThenRemoveAutoSaveFile
MergeWithoutSavingAndKeepAutoSaveFile
DontMergeAndRemoveAutoSaveFile
DontMergeAndKeepAutoSaveFile
}
}
%% Event Args Classes
namespace Upsilon.Apps.Passkey.Interfaces.Events {
class AutoSaveDetectedEventArgs {
+MergeBehavior : AutoSaveMergeBehavior
}
class WarningDetectedEventArgs {
+Warnings : IEnumerable~IWarning~
}
class LogoutEventArgs {
+LoginTimeoutReached : bool
}
}
%% Inheritance Relations
IUser --|> IItem
IService --|> IItem
IAccount --|> IItem
%% Link Relations
IItem --> IDatabase : Database
IAccount --> IService : Service
IAccount --> AccountOption : Options
IService "0" --> "*" IAccount : Accounts
IService --> IUser : User
IUser "0" --> "*" IService : Services
IDatabase --> ISerializationCenter : SerializationCenter
IDatabase --> ICryptographyCenter : CryptographyCenter
IDatabase --> IPasswordFactory : PasswordFactory
IDatabase --> IClipboardManager : ClipboardManager
IDatabase --> IUser : User
IDatabase "0" --> "*" IWarning : Warnings
IDatabase "0" --> "*" ILog : Logs
IDatabase --> WarningDetectedEventArgs : WarningDetected
IDatabase --> AutoSaveDetectedEventArgs : AutoSaveDetected
IDatabase --> LogoutEventArgs : DatabaseClosed
IWarning --> WarningType : WarningType
IWarning "0" --> "*" ILog : Logs
IWarning "0" --> "*" IAccount : Accounts
AutoSaveDetectedEventArgs --> AutoSaveMergeBehavior : MergeBehavior
WarningDetectedEventArgs "0" --> "*" IWarning : Warnings
Example Use Cases
To create a new database, use the Upsilon.Apps.Passkey.Core.Models.Database.Create static method.
This method needs an ICryptographyCenter implementation, an ISerializationCenter implementation, an IPasswordFactory implementation and an IClipboardManager implementation.
The namespace Upsilon.Apps.Passkey.Core.Utils already contains implementations for all of these intefaces except for the IClipboardManager which needs an OS specific implementation.
The next parameter is the database file itself, which will be created during the process.
Finally, the method take the username and the passkeys. Note that the passkeys are used as master passwords to encrypt the database (and the other files).
IDatabase database = Upsilon.Apps.Passkey.Core.Models.Database.Create(new Upsilon.Apps.Passkey.Core.Utils.CryptographyCenter(),
new Upsilon.Apps.Passkey.Core.Utils.JsonSerializationCenter(),
new Upsilon.Apps.Passkey.Core.Utils.PasswordFactory(),
new OSSpecificClipboardManager(),
"./database.pku",
"username",
new string[] { "master_password_1", "master_password_2", "master_password_3" });After creation, the method will directly open the database but it will not login directly to the current user. So to login, check the Login to an user use case.
To open an existing database, use the Upsilon.Apps.Passkey.Core.Models.Database.Open static method.
This method needs an ICryptographyCenter implementation, an ISerializationCenter implementation, an IPasswordFactory implementation and an IClipboardManager implementation as in the creation step.
The next parameter is the database file itself and must, obviously, exist.
Finally, the method take the username.
IDatabase database = Upsilon.Apps.Passkey.Core.Models.Database.Open(new Upsilon.Apps.Passkey.Core.Utils.CryptographyCenter(),
new Upsilon.Apps.Passkey.Core.Utils.JsonSerializationCenter(),
new Upsilon.Apps.Passkey.Core.Utils.PasswordFactory(),
new OSSpecificClipboardManager(),
"./database.pku",
"username");After opening (or creating) a database, use the IDatabase.Login method to login the user.
To do that, call the login method with every passkeys used during the database creation process.
Only the last call of that method, with every correct and ordered passkeys, will return the IUser representing the current user successfuly loged in.
Else that method will return null.
IUser? user = database.Login("master_password_1"); // Will return null
user = database.Login("master_password_2"); // Will also return null
user = database.Login("master_password_3"); // Will return a IUser this timeOnce the IUser retrieved, it allow a full access to all services and accounts, all log history and all user parameters.
Use the IDatabase.Save method to save the user's updates.
Note that any update on the user, its services and/or accounts which is not saved will be keeped in a hiden autosave file.
user.LogoutTimeout = 5; // Setting the logout timeout to 5 min will create a hiden autosave file
database.Save(); // Will save the new logout timeout in the database file and remove the autosave fileTo logout and close the database, use the IDatabase.Close method.
All unsaved updates are stored inside the hiden autosave file.
database.Close();- Clone the repository:
git clone https://github.com/YassinLokhat/Upsilon.Apps.Passkey.git -
- Build the solution for Windows users:
dotnet build Upsilon.Apps.Passkey.Windows.slnx
- Build the solution for Windows users:
-
- Build the solution for Linux users:
dotnet build Upsilon.Apps.Passkey.Linux.slnx
- Build the solution for Linux users:
Contributions are welcome! Please submit a pull request with your changes.
This project is licensed under the MIT License. See the LICENSE file for details.