mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-10-04 08:00:16 -04:00
Compare commits
40 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fa3d38db18 | ||
dd1e128e17 | |||
d247aa8334 | |||
5f995a10c1 | |||
![]() |
b89998c053 | ||
d10f617910 | |||
1b439a4960 | |||
![]() |
5cf4ff3012 | ||
46c8429cde | |||
8cd3801897 | |||
b47d7fb69b | |||
52e08d8c98 | |||
ad0d8d6c97 | |||
5b31d3ff72 | |||
2bbd931b1a | |||
8c09d266e8 | |||
a11d209280 | |||
![]() |
e95e62f184 | ||
![]() |
84e7afc819 | ||
![]() |
c82d6d001d | ||
d5b7845242 | |||
9de9ae54da | |||
6548d1d9c1 | |||
54ad395d13 | |||
6bb37d9e70 | |||
66fd87124b | |||
7bb78fd374 | |||
dc62cedb74 | |||
![]() |
bc6e7d0097 | ||
01ed1bc9c1 | |||
![]() |
19008cdad2 | ||
8a312bec71 | |||
2b8d37057c | |||
5497e6fc00 | |||
86064af3a2 | |||
3265244f06 | |||
2698070328 | |||
5638b59fda | |||
9f94dd55c2 | |||
0ded991673 |
@@ -1,4 +1,6 @@
|
||||
using Windows.UI.Xaml;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Microsoft.Xaml.Interactivity;
|
||||
|
||||
@@ -13,11 +15,13 @@ namespace ModernKeePass.Actions
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TargetObjectProperty =
|
||||
DependencyProperty.Register("TargetObject", typeof(Control), typeof(SetupFocusAction), new PropertyMetadata(0));
|
||||
DependencyProperty.Register("TargetObject", typeof(Control), typeof(SetupFocusAction), new PropertyMetadata(null));
|
||||
|
||||
public object Execute(object sender, object parameter)
|
||||
{
|
||||
return TargetObject?.Focus(FocusState.Programmatic);
|
||||
return Task.Factory.StartNew(
|
||||
() => Dispatcher.RunAsync(CoreDispatcherPriority.Low,
|
||||
() => TargetObject?.Focus(FocusState.Programmatic)));
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,6 +8,8 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Styles/TextBoxWithButtonStyle.xaml" />
|
||||
<ResourceDictionary Source="Styles/HamburgerButtonStyle.xaml" />
|
||||
<ResourceDictionary Source="Styles/ListViewLeftIndicatorStyle.xaml" />
|
||||
<ResourceDictionary Source="Styles/NoBorderButtonStyle.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
|
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.ApplicationModel.Search;
|
||||
using Windows.Foundation;
|
||||
using Windows.Data.Json;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
using ModernKeePass.Common;
|
||||
using ModernKeePass.Interfaces;
|
||||
|
||||
// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
|
||||
|
||||
@@ -17,9 +17,10 @@ namespace ModernKeePass
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
sealed partial class App : Application
|
||||
sealed partial class App
|
||||
{
|
||||
public DatabaseHelper Database { get; set; } = new DatabaseHelper();
|
||||
public Dictionary<string, IPwEntity> PendingDeleteEntities = new Dictionary<string, IPwEntity>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
@@ -31,6 +32,7 @@ namespace ModernKeePass
|
||||
Suspending += OnSuspending;
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched normally by the end user. Other entry points
|
||||
/// will be used such as when the application is launched to open a specific file.
|
||||
@@ -38,11 +40,21 @@ namespace ModernKeePass
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs e)
|
||||
{
|
||||
OnLaunchOrActivated(e);
|
||||
}
|
||||
|
||||
protected override void OnActivated(IActivatedEventArgs args)
|
||||
{
|
||||
OnLaunchOrActivated(args);
|
||||
}
|
||||
|
||||
private void OnLaunchOrActivated(IActivatedEventArgs e)
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
if (System.Diagnostics.Debugger.IsAttached)
|
||||
{
|
||||
DebugSettings.EnableFrameRateCounter = true;
|
||||
//DebugSettings.EnableFrameRateCounter = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -53,9 +65,8 @@ namespace ModernKeePass
|
||||
if (rootFrame == null)
|
||||
{
|
||||
// Create a Frame to act as the navigation context and navigate to the first page
|
||||
rootFrame = new Frame();
|
||||
rootFrame = new Frame {Language = Windows.Globalization.ApplicationLanguages.Languages[0]};
|
||||
// Set the default language
|
||||
rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];
|
||||
|
||||
rootFrame.NavigationFailed += OnNavigationFailed;
|
||||
|
||||
@@ -68,13 +79,30 @@ namespace ModernKeePass
|
||||
Window.Current.Content = rootFrame;
|
||||
}
|
||||
|
||||
if (rootFrame.Content == null)
|
||||
if (e is LaunchActivatedEventArgs)
|
||||
{
|
||||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
rootFrame.Navigate(typeof(MainPage), e.Arguments);
|
||||
var lauchActivatedEventArgs = (LaunchActivatedEventArgs) e;
|
||||
if (rootFrame.Content == null)
|
||||
{
|
||||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
rootFrame.Navigate(typeof(Pages.MainPage), lauchActivatedEventArgs.Arguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
// App is "launched" via the Toast Activation event
|
||||
UndoEntityDelete(lauchActivatedEventArgs.Arguments);
|
||||
}
|
||||
}
|
||||
// This is only available on Windows 10...
|
||||
/*else if (e is ToastNotificationActivatedEventArgs)
|
||||
{
|
||||
var toastActivationArgs = e as ToastNotificationActivatedEventArgs;
|
||||
|
||||
// Parse the query string (using QueryString.NET)
|
||||
UndoEntityDelete(QueryString.Parse(toastActivationArgs.Argument));
|
||||
}*/
|
||||
// Ensure the current window is active
|
||||
Window.Current.Activate();
|
||||
}
|
||||
@@ -99,19 +127,32 @@ namespace ModernKeePass
|
||||
private void OnSuspending(object sender, SuspendingEventArgs e)
|
||||
{
|
||||
var deferral = e.SuspendingOperation.GetDeferral();
|
||||
//TODO: Save application state and stop any background activity
|
||||
//Database.Save();
|
||||
Database.Save();
|
||||
deferral.Complete();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when application is launched from opening a file in Windows Explorer
|
||||
/// </summary>
|
||||
/// <param name="args">Details about the file being opened</param>
|
||||
protected override void OnFileActivated(FileActivatedEventArgs args)
|
||||
{
|
||||
base.OnFileActivated(args);
|
||||
var rootFrame = new Frame();
|
||||
Database.DatabaseFile = args.Files[0] as StorageFile;
|
||||
rootFrame.Navigate(typeof(MainPage), args);
|
||||
rootFrame.Navigate(typeof(Pages.MainPage), args);
|
||||
Window.Current.Content = rootFrame;
|
||||
Window.Current.Activate();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void UndoEntityDelete(string arguments)
|
||||
{
|
||||
if (arguments == null) return;
|
||||
var args = JsonObject.Parse(arguments);
|
||||
var entity = PendingDeleteEntities[args["entityId"].GetString()];
|
||||
PendingDeleteEntities.Remove(args["entityId"].GetString());
|
||||
entity.UndoDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
MainPackage=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.2.0.14_AnyCPU.appx
|
||||
SymbolPackage=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\AppPackages\ModernKeePass_1.2.0.14_Test\ModernKeePass_1.2.0.14_AnyCPU.appxsym
|
||||
ResourcePack=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.2.0.14_scale-140.appx
|
||||
ResourcePack=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.2.0.14_scale-180.appx
|
||||
MainPackage=C:\Users\bg45\source\repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.5.0.25_AnyCPU.appx
|
||||
SymbolPackage=C:\Users\bg45\source\repos\ModernKeePass\ModernKeePass\AppPackages\ModernKeePass_1.5.0.25_Test\ModernKeePass_1.5.0.25_AnyCPU.appxsym
|
||||
ResourcePack=C:\Users\bg45\source\repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.5.0.25_scale-140.appx
|
||||
ResourcePack=C:\Users\bg45\source\repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.5.0.25_scale-180.appx
|
||||
|
@@ -33,6 +33,12 @@ namespace ModernKeePass.Common
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a KeePass database
|
||||
/// </summary>
|
||||
/// <param name="password">The database password</param>
|
||||
/// <param name="createNew">True to create a new database before opening it</param>
|
||||
/// <returns>An error message, if any</returns>
|
||||
public string Open(string password, bool createNew = false)
|
||||
{
|
||||
var key = new CompositeKey();
|
||||
@@ -64,6 +70,10 @@ namespace ModernKeePass.Common
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current database to another file and open it
|
||||
/// </summary>
|
||||
/// <param name="file">The new database file</param>
|
||||
internal void Save(StorageFile file)
|
||||
{
|
||||
DatabaseFile = file;
|
||||
@@ -71,12 +81,18 @@ namespace ModernKeePass.Common
|
||||
Status = DatabaseStatus.Opened;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commit the changes to the currently opened database to file
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
if (_pwDatabase != null && _pwDatabase.IsOpen)
|
||||
_pwDatabase.Save(new NullStatusLogger());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the currently opened database
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
_pwDatabase?.Close();
|
||||
|
35
ModernKeePass/Common/MessageDialogHelper.cs
Normal file
35
ModernKeePass/Common/MessageDialogHelper.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Windows.UI.Popups;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using ModernKeePass.Interfaces;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
namespace ModernKeePass.Common
|
||||
{
|
||||
public static class MessageDialogHelper
|
||||
{
|
||||
public static async void ShowDeleteConfirmationDialog(string text, IPwEntity model, Frame backFrame)
|
||||
{
|
||||
// Create the message dialog and set its content
|
||||
var messageDialog = new MessageDialog(text);
|
||||
|
||||
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
|
||||
messageDialog.Commands.Add(new UICommand("Delete", delete =>
|
||||
{
|
||||
ToastNotificationHelper.ShowUndoToast(model);
|
||||
model.MarkForDelete();
|
||||
if (backFrame.CanGoBack) backFrame.GoBack();
|
||||
}));
|
||||
messageDialog.Commands.Add(new UICommand("Cancel"));
|
||||
|
||||
// Set the command that will be invoked by default
|
||||
messageDialog.DefaultCommandIndex = 1;
|
||||
|
||||
// Set the command to be invoked when escape is pressed
|
||||
messageDialog.CancelCommandIndex = 1;
|
||||
|
||||
// Show the message dialog
|
||||
await messageDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
102
ModernKeePass/Common/ToastNotificationHelper.cs
Normal file
102
ModernKeePass/Common/ToastNotificationHelper.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using Windows.Data.Json;
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.UI.Notifications;
|
||||
using Windows.UI.Xaml;
|
||||
using ModernKeePass.Interfaces;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
namespace ModernKeePass.Common
|
||||
{
|
||||
public static class ToastNotificationHelper
|
||||
{
|
||||
public static /*async*/ void ShowUndoToast(IPwEntity entity)
|
||||
{
|
||||
// This is for Windows 10
|
||||
// Construct the visuals of the toast
|
||||
/*var visual = new ToastVisual
|
||||
{
|
||||
BindingGeneric = new ToastBindingGeneric
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new AdaptiveText
|
||||
{
|
||||
Text = $"{entityType} {entity.Name} deleted."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Construct the actions for the toast (inputs and buttons)
|
||||
var actions = new ToastActionsCustom
|
||||
{
|
||||
Buttons =
|
||||
{
|
||||
new ToastButton("Undo", new QueryString
|
||||
{
|
||||
{ "action", "undo" },
|
||||
{ "entityType", entityType },
|
||||
{ "entityId", entity.Id }
|
||||
|
||||
}.ToString())
|
||||
}
|
||||
};
|
||||
|
||||
// Now we can construct the final toast content
|
||||
var toastContent = new ToastContent
|
||||
{
|
||||
Visual = visual,
|
||||
Actions = actions,
|
||||
// Arguments when the user taps body of toast
|
||||
Launch = new QueryString()
|
||||
{
|
||||
{ "action", "undo" },
|
||||
{ "entityType", "group" },
|
||||
{ "entityId", entity.Id }
|
||||
|
||||
}.ToString()
|
||||
};
|
||||
|
||||
// And create the toast notification
|
||||
var toastXml = new XmlDocument();
|
||||
toastXml.LoadXml(toastContent.GetContent());
|
||||
|
||||
var toast = new ToastNotification(toastXml) {ExpirationTime = DateTime.Now.AddSeconds(5)};
|
||||
toast.Dismissed += Toast_Dismissed;
|
||||
*/
|
||||
|
||||
var entityType = entity is GroupVm ? "Group" : "Entry";
|
||||
var notificationXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
|
||||
var toastElements = notificationXml.GetElementsByTagName("text");
|
||||
toastElements[0].AppendChild(notificationXml.CreateTextNode($"{entityType} {entity.Name} deleted"));
|
||||
toastElements[1].AppendChild(notificationXml.CreateTextNode("Click me to undo"));
|
||||
var toastNode = notificationXml.SelectSingleNode("/toast");
|
||||
|
||||
var launch = new JsonObject
|
||||
{
|
||||
{"entityType", JsonValue.CreateStringValue(entityType)},
|
||||
{"entityId", JsonValue.CreateStringValue(entity.Id)}
|
||||
};
|
||||
((XmlElement)toastNode)?.SetAttribute("launch", launch.Stringify());
|
||||
|
||||
var toast = new ToastNotification(notificationXml)
|
||||
{
|
||||
ExpirationTime = DateTime.Now.AddSeconds(5)
|
||||
};
|
||||
toast.Dismissed += Toast_Dismissed;
|
||||
ToastNotificationManager.CreateToastNotifier().Show(toast);
|
||||
}
|
||||
|
||||
private static void Toast_Dismissed(ToastNotification sender, ToastDismissedEventArgs args)
|
||||
{
|
||||
var toastNode = sender.Content.SelectSingleNode("/toast");
|
||||
if (toastNode == null) return;
|
||||
var launchArguments = JsonObject.Parse(((XmlElement)toastNode).GetAttribute("launch"));
|
||||
var app = (App)Application.Current;
|
||||
var entity = app.PendingDeleteEntities[launchArguments["entityId"].GetString()];
|
||||
app.PendingDeleteEntities.Remove(launchArguments["entityId"].GetString());
|
||||
entity.CommitDelete();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,13 +1,17 @@
|
||||
<UserControl
|
||||
<UserControl x:Name="UserControl"
|
||||
x:Class="ModernKeePass.Controls.OpenDatabaseUserControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:actions="using:ModernKeePass.Actions"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="65"
|
||||
d:DesignWidth="335" Loaded="UserControl_Loaded">
|
||||
d:DesignHeight="60"
|
||||
d:DesignWidth="550" >
|
||||
<UserControl.Resources>
|
||||
<SolidColorBrush x:Key="ErrorColorBrush" Color="Red"/>
|
||||
<Style TargetType="PasswordBox" x:Name="PasswordBoxWithButtonStyle">
|
||||
<Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}" />
|
||||
<Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}" />
|
||||
@@ -36,33 +40,25 @@
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
@@ -238,6 +234,14 @@
|
||||
To="{ThemeResource TextControlPointerOverBorderThemeOpacity}" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Error">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ErrorColorBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Focused" />
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ButtonStates">
|
||||
@@ -333,12 +337,13 @@
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<StackPanel>
|
||||
<PasswordBox x:Name="PasswordBox" Width="300" IsPasswordRevealButtonEnabled="True" KeyDown="PasswordBox_KeyDown" PlaceholderText="Password" Style="{StaticResource PasswordBoxWithButtonStyle}"/>
|
||||
<!--<StackPanel Orientation="Horizontal" Margin="0,-1,0,-1" >
|
||||
<Button Click="OpenButton_OnClick" Width="auto" Padding="2,0">
|
||||
<SymbolIcon Symbol="Forward" />
|
||||
</Button>
|
||||
</StackPanel>-->
|
||||
<TextBlock x:Name="StatusTextBlock" Height="32" Width="auto" Foreground="#FFBF6969" FontSize="16" FontWeight="Bold" />
|
||||
<PasswordBox x:Name="PasswordBox" Password="{Binding Password, ElementName=UserControl, Mode=TwoWay}" IsPasswordRevealButtonEnabled="True" KeyDown="PasswordBox_KeyDown" PlaceholderText="Password" Style="{StaticResource PasswordBoxWithButtonStyle}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:DataTriggerBehavior Binding="{Binding Visibility, ElementName=UserControl}" Value="Visible">
|
||||
<actions:SetupFocusAction TargetObject="{Binding ElementName=PasswordBox}" />
|
||||
</core:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</PasswordBox>
|
||||
<TextBlock x:Name="StatusTextBlock" Height="28" Foreground="{ThemeResource ErrorColorBrush}" FontSize="14" FontWeight="Light" TextWrapping="WrapWholeWords" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
@@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.System;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using ModernKeePass.Common;
|
||||
using ModernKeePass.Events;
|
||||
@@ -12,7 +9,7 @@ using ModernKeePass.Events;
|
||||
|
||||
namespace ModernKeePass.Controls
|
||||
{
|
||||
public sealed partial class OpenDatabaseUserControl : UserControl
|
||||
public sealed partial class OpenDatabaseUserControl
|
||||
{
|
||||
public bool CreateNew
|
||||
{
|
||||
@@ -24,7 +21,19 @@ namespace ModernKeePass.Controls
|
||||
"CreateNew",
|
||||
typeof(bool),
|
||||
typeof(OpenDatabaseUserControl),
|
||||
new PropertyMetadata(null, (o, args) => { }));
|
||||
new PropertyMetadata(false, (o, args) => { }));
|
||||
|
||||
public string Password
|
||||
{
|
||||
get { return (string)GetValue(PasswordProperty); }
|
||||
set { SetValue(PasswordProperty, value); }
|
||||
}
|
||||
public static readonly DependencyProperty PasswordProperty =
|
||||
DependencyProperty.Register(
|
||||
"Password",
|
||||
typeof(string),
|
||||
typeof(OpenDatabaseUserControl),
|
||||
new PropertyMetadata(string.Empty, (o, args) => { }));
|
||||
|
||||
public OpenDatabaseUserControl()
|
||||
{
|
||||
@@ -42,19 +51,23 @@ namespace ModernKeePass.Controls
|
||||
var app = (App)Application.Current;
|
||||
StatusTextBlock.Text = app.Database.Open(PasswordBox.Password, CreateNew);
|
||||
if (app.Database.Status == DatabaseHelper.DatabaseStatus.Opened)
|
||||
{
|
||||
ValidationChecked?.Invoke(this, new PasswordEventArgs(app.Database.RootGroup));
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(PasswordBox, "Error", true);
|
||||
}
|
||||
}
|
||||
|
||||
private void PasswordBox_KeyDown(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
if (e.Key == VirtualKey.Enter) OpenButton_OnClick(null, null);
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Task.Factory.StartNew(
|
||||
() => Dispatcher.RunAsync(CoreDispatcherPriority.Low,
|
||||
() => PasswordBox.Focus(FocusState.Programmatic)));
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(PasswordBox, "Normal", true);
|
||||
StatusTextBlock.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace ModernKeePass.Converters
|
||||
{
|
||||
public class DoubleToForegroungBrushComplexityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
try
|
||||
{
|
||||
var currentValue = (double) value;
|
||||
var maxValue = double.Parse(parameter as string);
|
||||
var green = System.Convert.ToByte(currentValue / maxValue * byte.MaxValue);
|
||||
var red = (byte) (byte.MaxValue - green);
|
||||
return new SolidColorBrush(Color.FromArgb(255, red, green, 0));
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
return new SolidColorBrush(Color.FromArgb(255, 0, byte.MaxValue, 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
27
ModernKeePass/Converters/ProgressBarLegalValuesConverter.cs
Normal file
27
ModernKeePass/Converters/ProgressBarLegalValuesConverter.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace ModernKeePass.Converters
|
||||
{
|
||||
public class ProgressBarLegalValuesConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
var legalValuesOptionString = parameter as string;
|
||||
var legalValuesOptions = legalValuesOptionString?.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (legalValuesOptions == null || legalValuesOptions.Length != 2) return 0;
|
||||
|
||||
var minValue = double.Parse(legalValuesOptions[0]);
|
||||
var maxValue = double.Parse(legalValuesOptions[1]);
|
||||
var count = value is double ? (double)value : 0;
|
||||
if (count > maxValue) return maxValue;
|
||||
if (count < minValue) return minValue;
|
||||
return count;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
24
ModernKeePass/Converters/TextToFontStyleConverter.cs
Normal file
24
ModernKeePass/Converters/TextToFontStyleConverter.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace ModernKeePass.Converters
|
||||
{
|
||||
public class TextToFontStyleConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
var compareValue = parameter as string;
|
||||
var text = value as string;
|
||||
return string.Compare(text, compareValue, StringComparison.OrdinalIgnoreCase) == 0
|
||||
? FontStyle.Italic
|
||||
: FontStyle.Normal;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
20
ModernKeePass/Converters/TextToWidthConverter.cs
Normal file
20
ModernKeePass/Converters/TextToWidthConverter.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace ModernKeePass.Converters
|
||||
{
|
||||
public class TextToWidthConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
var fontSize = double.Parse(parameter as string);
|
||||
var text = value as string;
|
||||
return text?.Length * fontSize ?? 0;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
7
ModernKeePass/Interfaces/IHasSelectableObject.cs
Normal file
7
ModernKeePass/Interfaces/IHasSelectableObject.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace ModernKeePass.Interfaces
|
||||
{
|
||||
public interface IHasSelectableObject
|
||||
{
|
||||
ISelectableModel SelectedItem { get; set; }
|
||||
}
|
||||
}
|
19
ModernKeePass/Interfaces/IPwEntity.cs
Normal file
19
ModernKeePass/Interfaces/IPwEntity.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
namespace ModernKeePass.Interfaces
|
||||
{
|
||||
public interface IPwEntity
|
||||
{
|
||||
GroupVm ParentGroup { get; }
|
||||
Symbol IconSymbol { get; }
|
||||
string Id { get; }
|
||||
string Name { get; set; }
|
||||
bool IsEditMode { get; }
|
||||
|
||||
void CommitDelete();
|
||||
void UndoDelete();
|
||||
void Save();
|
||||
void MarkForDelete();
|
||||
}
|
||||
}
|
7
ModernKeePass/Interfaces/ISelectableModel.cs
Normal file
7
ModernKeePass/Interfaces/ISelectableModel.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace ModernKeePass.Interfaces
|
||||
{
|
||||
public interface ISelectableModel
|
||||
{
|
||||
bool IsSelected { get; set; }
|
||||
}
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
xmlns:controls="using:ModernKeePass.Controls"
|
||||
x:Class="ModernKeePass.MainPage"
|
||||
mc:Ignorable="d"
|
||||
Background="{StaticResource ApplicationPageBackgroundThemeBrush}" >
|
||||
<Page.DataContext>
|
||||
<viewModels:MainVm />
|
||||
</Page.DataContext>
|
||||
<Page.Resources>
|
||||
<CollectionViewSource
|
||||
x:Name="MenuItemsSource"
|
||||
Source="{Binding MainMenuItems}"
|
||||
IsSourceGrouped="True" />
|
||||
</Page.Resources>
|
||||
|
||||
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="50" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<controls:ListViewWithDisable Grid.Column="0"
|
||||
x:Name="MenuListView"
|
||||
RequestedTheme="Dark"
|
||||
SelectionChanged="ListView_SelectionChanged"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
ItemsSource="{Binding Source={StaticResource MenuItemsSource}}"
|
||||
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||
IsSynchronizedWithCurrentItem="False">
|
||||
<ListView.Header>
|
||||
<TextBlock Text="ModernKeePass" FontWeight="Bold" FontSize="36" Margin="20" />
|
||||
</ListView.Header>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate >
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<SymbolIcon Symbol="{Binding SymbolIcon}" />
|
||||
<TextBlock Text="{Binding Title}" Margin="10,5,0,0" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="Padding" Value="20,5,0,0" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.GroupStyle>
|
||||
<GroupStyle HidesIfEmpty="True">
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Background="DarkGray" Margin="20,0,0,0">
|
||||
<Border Height="1" Width="240" HorizontalAlignment="Stretch"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>
|
||||
</ListView.GroupStyle>
|
||||
</controls:ListViewWithDisable>
|
||||
<Frame Grid.Column="2" Name="MenuFrame" Width="auto" Margin="0,60,0,0" />
|
||||
</Grid>
|
||||
</Page>
|
@@ -1,31 +0,0 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace ModernKeePass
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
DataContext = new MainVm(Frame, MenuFrame);
|
||||
}
|
||||
|
||||
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
var mainVm = DataContext as MainVm;
|
||||
mainVm.SelectedItem?.Destination.Navigate(mainVm.SelectedItem.PageType, mainVm.SelectedItem.Parameter);
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,18 +10,19 @@ namespace ModernKeePass.Mappings
|
||||
switch (icon)
|
||||
{
|
||||
case PwIcon.Key: return Symbol.Permissions;
|
||||
case PwIcon.WorldSocket:
|
||||
case PwIcon.World: return Symbol.World;
|
||||
case PwIcon.Warning: return Symbol.Important;
|
||||
case PwIcon.WorldComputer:
|
||||
case PwIcon.Drive:
|
||||
case PwIcon.DriveWindows:
|
||||
case PwIcon.NetworkServer: return Symbol.MapDrive;
|
||||
//case PwIcon.MarkedDirectory: return Symbol.;
|
||||
case PwIcon.MarkedDirectory: return Symbol.Map;
|
||||
case PwIcon.UserCommunication: return Symbol.ContactInfo;
|
||||
//case PwIcon.Parts: return Symbol.;
|
||||
case PwIcon.Parts: return Symbol.ViewAll;
|
||||
case PwIcon.Notepad: return Symbol.Document;
|
||||
//case PwIcon.WorldScoket: return Symbol.;
|
||||
case PwIcon.Identity: return Symbol.Contact2;
|
||||
//case PwIcon.PaperReady: return Symbol.;
|
||||
case PwIcon.PaperReady: return Symbol.SyncFolder;
|
||||
case PwIcon.Digicam: return Symbol.Camera;
|
||||
case PwIcon.IRCommunication: return Symbol.View;
|
||||
case PwIcon.Energy: return Symbol.ZeroBars;
|
||||
@@ -36,17 +37,16 @@ namespace ModernKeePass.Mappings
|
||||
case PwIcon.Screen: return Symbol.GoToStart;
|
||||
case PwIcon.EnergyCareful: return Symbol.FourBars;
|
||||
case PwIcon.Disk: return Symbol.Save;
|
||||
//case PwIcon.Drive: return Symbol.;
|
||||
//case PwIcon.PaperQ: return Symbol.;
|
||||
//case PwIcon.TerminalEncrypted: return Symbol.;
|
||||
//case PwIcon.Console: return Symbol.;
|
||||
//case PwIcon.Printer: return Symbol.;
|
||||
case PwIcon.Console: return Symbol.SlideShow;
|
||||
case PwIcon.Printer: return Symbol.Scan;
|
||||
case PwIcon.ProgramIcons: return Symbol.GoToStart;
|
||||
//case PwIcon.Run: return Symbol.;
|
||||
case PwIcon.Settings:
|
||||
case PwIcon.Tool: return Symbol.Repair;
|
||||
//case PwIcon.Archive: return Symbol.;
|
||||
case PwIcon.Count: return Symbol.MapDrive;
|
||||
case PwIcon.Archive: return Symbol.Crop;
|
||||
case PwIcon.Count: return Symbol.Calculator;
|
||||
case PwIcon.Clock: return Symbol.Clock;
|
||||
case PwIcon.EMailSearch: return Symbol.Find;
|
||||
case PwIcon.PaperFlag: return Symbol.Flag;
|
||||
@@ -72,10 +72,10 @@ namespace ModernKeePass.Mappings
|
||||
//case PwIcon.Feather: return Symbol.;
|
||||
//case PwIcon.Apple: return Symbol.;
|
||||
//case PwIcon.Wiki: return Symbol.;
|
||||
//case PwIcon.Money: return Symbol.;
|
||||
case PwIcon.Money: return Symbol.Shop;
|
||||
case PwIcon.Certificate: return Symbol.PreviewLink;
|
||||
case PwIcon.BlackBerry: return Symbol.CellPhone;
|
||||
default: return Symbol.More;
|
||||
default: return Symbol.Stop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@
|
||||
<PackageCertificateThumbprint>ED3AA34F46D03498F989901C5DB2742B65D72F60</PackageCertificateThumbprint>
|
||||
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
|
||||
<AppxBundlePlatforms>neutral</AppxBundlePlatforms>
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -112,12 +113,17 @@
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Common\DatabaseHelper.cs" />
|
||||
<Compile Include="Common\MessageDialogHelper.cs" />
|
||||
<Compile Include="Common\NavigationHelper.cs" />
|
||||
<Compile Include="Common\NotifyPropertyChangedBase.cs" />
|
||||
<Compile Include="Common\ObservableDictionary.cs" />
|
||||
<Compile Include="Common\RelayCommand.cs" />
|
||||
<Compile Include="Common\SuspensionManager.cs" />
|
||||
<Compile Include="Controls\FirstItemDataTemplateSelector.cs" />
|
||||
<Compile Include="Common\ToastNotificationHelper.cs" />
|
||||
<Compile Include="Interfaces\IHasSelectableObject.cs" />
|
||||
<Compile Include="Interfaces\ISelectableModel.cs" />
|
||||
<Compile Include="Pages\BasePages\LayoutAwarePageBase.cs" />
|
||||
<Compile Include="TemplateSelectors\FirstItemDataTemplateSelector.cs" />
|
||||
<Compile Include="Controls\ListViewWithDisable.cs" />
|
||||
<Compile Include="Controls\OpenDatabaseUserControl.xaml.cs">
|
||||
<DependentUpon>OpenDatabaseUserControl.xaml</DependentUpon>
|
||||
@@ -125,17 +131,32 @@
|
||||
<Compile Include="Controls\TextBoxWithButton.cs" />
|
||||
<Compile Include="Converters\BooleanToVisibilityConverter.cs" />
|
||||
<Compile Include="Converters\ColorToBrushConverter.cs" />
|
||||
<Compile Include="Converters\DoubleToForegroungBrushComplexityConverter.cs" />
|
||||
<Compile Include="Converters\InverseBooleanToVisibilityConverter.cs" />
|
||||
<Compile Include="Converters\TextToFontStyleConverter.cs" />
|
||||
<Compile Include="Converters\PluralizationConverter.cs" />
|
||||
<Compile Include="Converters\ProgressBarLegalValuesConverter.cs" />
|
||||
<Compile Include="Converters\TextToWidthConverter.cs" />
|
||||
<Compile Include="Events\PasswordEventArgs.cs" />
|
||||
<Compile Include="Interfaces\IIsEnabled.cs" />
|
||||
<Compile Include="MainPage.xaml.cs">
|
||||
<Compile Include="Interfaces\IPwEntity.cs" />
|
||||
<Compile Include="Pages\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Mappings\PwIconToSegoeMapping.cs" />
|
||||
<Compile Include="Pages\NewDatabasePage.xaml.cs">
|
||||
<Compile Include="Pages\MainPageFrames\AboutPage.xaml.cs">
|
||||
<DependentUpon>AboutPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\MainPageFrames\NewDatabasePage.xaml.cs">
|
||||
<DependentUpon>NewDatabasePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\SettingsPage.xaml.cs">
|
||||
<DependentUpon>SettingsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\MainPageFrames\WelcomePage.xaml.cs">
|
||||
<DependentUpon>WelcomePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ViewModels\AboutVm.cs" />
|
||||
<Compile Include="ViewModels\Items\MainMenuItemVm.cs" />
|
||||
<Compile Include="ViewModels\Items\RecentItemVm.cs" />
|
||||
<Compile Include="Pages\EntryDetailPage.xaml.cs">
|
||||
@@ -144,19 +165,20 @@
|
||||
<Compile Include="Pages\GroupDetailPage.xaml.cs">
|
||||
<DependentUpon>GroupDetailPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\OpenDatabasePage.xaml.cs">
|
||||
<Compile Include="Pages\MainPageFrames\OpenDatabasePage.xaml.cs">
|
||||
<DependentUpon>OpenDatabasePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\RecentDatabasesPage.xaml.cs">
|
||||
<Compile Include="Pages\MainPageFrames\RecentDatabasesPage.xaml.cs">
|
||||
<DependentUpon>RecentDatabasesPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\SaveDatabasePage.xaml.cs">
|
||||
<Compile Include="Pages\MainPageFrames\SaveDatabasePage.xaml.cs">
|
||||
<DependentUpon>SaveDatabasePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ViewModels\EntryVm.cs" />
|
||||
<Compile Include="ViewModels\GroupVm.cs" />
|
||||
<Compile Include="ViewModels\MainVm.cs" />
|
||||
<Compile Include="ViewModels\NewVm.cs" />
|
||||
<Compile Include="ViewModels\OpenVm.cs" />
|
||||
<Compile Include="ViewModels\RecentVm.cs" />
|
||||
<Compile Include="ViewModels\SaveVm.cs" />
|
||||
@@ -183,10 +205,14 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="MainPage.xaml">
|
||||
<Page Include="Pages\MainPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Pages\MainPageFrames\AboutPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\EntryDetailPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -195,19 +221,27 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\NewDatabasePage.xaml">
|
||||
<Page Include="Pages\MainPageFrames\NewDatabasePage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\OpenDatabasePage.xaml">
|
||||
<Page Include="Pages\MainPageFrames\OpenDatabasePage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\RecentDatabasesPage.xaml">
|
||||
<Page Include="Pages\MainPageFrames\RecentDatabasesPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\SaveDatabasePage.xaml">
|
||||
<Page Include="Pages\MainPageFrames\SaveDatabasePage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\SettingsPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\MainPageFrames\WelcomePage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
@@ -216,6 +250,16 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Page>
|
||||
<Page Include="Styles\ListViewLeftIndicatorStyle.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Page>
|
||||
<Page Include="Styles\NoBorderButtonStyle.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Page>
|
||||
<Page Include="Styles\TextBoxWithButtonStyle.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -227,8 +271,8 @@
|
||||
<HintPath>..\packages\Portable.BouncyCastle.1.8.1.3\lib\netstandard1.0\BouncyCastle.Crypto.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ModernKeePassLib, Version=2.28.1.4000, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ModernKeePassLib.2.28.4000\lib\netstandard1.2\ModernKeePassLib.dll</HintPath>
|
||||
<Reference Include="Microsoft.Toolkit.Uwp.Notifications, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Toolkit.Uwp.Notifications.2.0.0\lib\dotnet\Microsoft.Toolkit.Uwp.Notifications.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
@@ -291,19 +335,28 @@
|
||||
<Content Include="Assets\Wide310x150Logo.scale-180.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-80.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ModernKeePassLib\ModernKeePassLib.csproj">
|
||||
<Project>{2e710089-9559-4967-846c-e763dd1f3acb}</Project>
|
||||
<Name>ModernKeePassLib</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Pages\SettingsPageFrames\" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '12.0' ">
|
||||
<VisualStudioVersion>12.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<Import Project="..\packages\NETStandard.Library.2.0.0\build\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.0\build\NETStandard.Library.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\NETStandard.Library.2.0.0\build\NETStandard.Library.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NETStandard.Library.2.0.0\build\NETStandard.Library.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\NETStandard.Library.2.0.1\build\NETStandard.Library.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NETStandard.Library.2.0.1\build\NETStandard.Library.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
|
||||
<Import Project="..\packages\NETStandard.Library.2.0.1\build\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.1\build\NETStandard.Library.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
|
||||
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.2.0.14" />
|
||||
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.5.0.25" />
|
||||
<Properties>
|
||||
<DisplayName>ModernKeePass</DisplayName>
|
||||
<PublisherDisplayName>wismna</PublisherDisplayName>
|
||||
@@ -15,7 +15,7 @@
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="ModernKeePass.App">
|
||||
<m2:VisualElements DisplayName="ModernKeePass" Square150x150Logo="Assets\ModernKeePass-Logo.png" Square30x30Logo="Assets\ModernKeePass-SmallLogo.png" Description="A port of the KeePass application for the Windows store. For now, it only features read-only capabilites and only opens password protected databases." ForegroundText="light" BackgroundColor="#464646" ToastCapable="true">
|
||||
<m2:VisualElements DisplayName="ModernKeePass" Square150x150Logo="Assets\ModernKeePass-Logo.png" Square30x30Logo="Assets\ModernKeePass-SmallLogo.png" Description="A port of the KeePass application for the Windows store. You can create, open and edit KeePass 2.x password databases in a modern fashion." ForegroundText="light" BackgroundColor="#464646" ToastCapable="true">
|
||||
<m2:DefaultTile Square310x310Logo="Assets\Square310x310Logo.png" Wide310x150Logo="Assets\Wide310x150Logo.png" Square70x70Logo="Assets\Square70x70Logo.png">
|
||||
</m2:DefaultTile>
|
||||
<m2:SplashScreen Image="Assets\ModernKeePass-SplashScreen.png" />
|
||||
|
209
ModernKeePass/Pages/BasePages/LayoutAwarePageBase.cs
Normal file
209
ModernKeePass/Pages/BasePages/LayoutAwarePageBase.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using ModernKeePass.Common;
|
||||
using ModernKeePass.Interfaces;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
namespace ModernKeePass.Pages.BasePages
|
||||
{
|
||||
public class LayoutAwarePageBase: Page
|
||||
{
|
||||
/// <summary>
|
||||
/// NavigationHelper is used on each page to aid in navigation and
|
||||
/// process lifetime management
|
||||
/// </summary>
|
||||
public NavigationHelper NavigationHelper { get; }
|
||||
|
||||
public virtual ListView ListView { get; set; }
|
||||
public virtual CollectionViewSource ListViewSource { get; set; }
|
||||
public virtual IHasSelectableObject Model { get; set; }
|
||||
|
||||
public LayoutAwarePageBase()
|
||||
{
|
||||
// Setup the navigation helper
|
||||
NavigationHelper = new NavigationHelper(this);
|
||||
NavigationHelper.LoadState += navigationHelper_LoadState;
|
||||
NavigationHelper.SaveState += navigationHelper_SaveState;
|
||||
|
||||
// Setup the logical page navigation components that allow
|
||||
// the page to only show one pane at a time.
|
||||
NavigationHelper.GoBackCommand = new RelayCommand(() => GoBack(), () => CanGoBack());
|
||||
|
||||
// Start listening for Window size changes
|
||||
// to change from showing two panes to showing a single pane
|
||||
Window.Current.SizeChanged += Window_SizeChanged;
|
||||
InvalidateVisualState();
|
||||
}
|
||||
|
||||
protected void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
// Invalidate the view state when logical page navigation is in effect, as a change
|
||||
// in selection may cause a corresponding change in the current logical page. When
|
||||
// an item is selected this has the effect of changing from displaying the item list
|
||||
// to showing the selected item's details. When the selection is cleared this has the
|
||||
// opposite effect.
|
||||
if (!UsingLogicalPageNavigation()) return;
|
||||
NavigationHelper.GoBackCommand.RaiseCanExecuteChanged();
|
||||
InvalidateVisualState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the page with content passed during navigation. Any saved state is also
|
||||
/// provided when recreating a page from a prior session.
|
||||
/// </summary>
|
||||
/// <param name="sender">
|
||||
/// The source of the event; typically <see cref="Common.NavigationHelper"/>
|
||||
/// </param>
|
||||
/// <param name="e">Event data that provides both the navigation parameter passed to
|
||||
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested and
|
||||
/// a dictionary of state preserved by this page during an earlier
|
||||
/// session. The state will be null the first time a page is visited.</param>
|
||||
protected void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
|
||||
{
|
||||
// TODO: Assign a bindable group to Me.DefaultViewModel("Group")
|
||||
// TODO: Assign a collection of bindable items to Me.DefaultViewModel("Items")
|
||||
|
||||
if (e.PageState == null)
|
||||
{
|
||||
// When this is a new page, select the first item automatically unless logical page
|
||||
// navigation is being used (see the logical page navigation #region below.)
|
||||
if (!UsingLogicalPageNavigation() && ListViewSource.View != null)
|
||||
{
|
||||
ListViewSource.View.MoveCurrentToFirst();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restore the previously saved state associated with this page
|
||||
if (e.PageState.ContainsKey("SelectedItem") && ListViewSource.View != null)
|
||||
{
|
||||
ListViewSource.View.MoveCurrentTo(e.PageState["SelectedItem"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preserves state associated with this page in case the application is suspended or the
|
||||
/// page is discarded from the navigation cache. Values must conform to the serialization
|
||||
/// requirements of <see cref="Common.SuspensionManager.SessionState"/>.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event; typically <see cref="Common.NavigationHelper"/></param>
|
||||
/// <param name="e">Event data that provides an empty dictionary to be populated with
|
||||
/// serializable state.</param>
|
||||
protected void navigationHelper_SaveState(object sender, SaveStateEventArgs e)
|
||||
{
|
||||
if (ListViewSource.View != null)
|
||||
{
|
||||
e.PageState["SelectedItem"] = Model?.SelectedItem;
|
||||
}
|
||||
}
|
||||
|
||||
#region Logical page navigation
|
||||
|
||||
// The split page is designed so that when the Window does have enough space to show
|
||||
// both the list and the details, only one pane will be shown at at time.
|
||||
//
|
||||
// This is all implemented with a single physical page that can represent two logical
|
||||
// pages. The code below achieves this goal without making the user aware of the
|
||||
// distinction.
|
||||
|
||||
protected const int MinimumWidthForSupportingTwoPanes = 768;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked to determine whether the page should act as one logical page or two.
|
||||
/// </summary>
|
||||
/// <returns>True if the window should show act as one logical page, false
|
||||
/// otherwise.</returns>
|
||||
protected bool UsingLogicalPageNavigation()
|
||||
{
|
||||
return Window.Current.Bounds.Width < MinimumWidthForSupportingTwoPanes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked with the Window changes size
|
||||
/// </summary>
|
||||
/// <param name="sender">The current Window</param>
|
||||
/// <param name="e">Event data that describes the new size of the Window</param>
|
||||
protected void Window_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
|
||||
{
|
||||
InvalidateVisualState();
|
||||
}
|
||||
|
||||
protected bool CanGoBack()
|
||||
{
|
||||
if (UsingLogicalPageNavigation() && ListView.SelectedItem != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return NavigationHelper.CanGoBack();
|
||||
}
|
||||
protected void GoBack()
|
||||
{
|
||||
if (UsingLogicalPageNavigation() && ListView.SelectedItem != null)
|
||||
{
|
||||
// When logical page navigation is in effect and there's a selected item that
|
||||
// item's details are currently displayed. Clearing the selection will return to
|
||||
// the item list. From the user's point of view this is a logical backward
|
||||
// navigation.
|
||||
ListView.SelectedItem = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationHelper.GoBack();
|
||||
}
|
||||
}
|
||||
|
||||
protected void InvalidateVisualState()
|
||||
{
|
||||
var visualState = DetermineVisualState();
|
||||
VisualStateManager.GoToState(this, visualState, false);
|
||||
NavigationHelper.GoBackCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked to determine the name of the visual state that corresponds to an application
|
||||
/// view state.
|
||||
/// </summary>
|
||||
/// <returns>The name of the desired visual state. This is the same as the name of the
|
||||
/// view state except when there is a selected item in portrait and snapped views where
|
||||
/// this additional logical page is represented by adding a suffix of _Detail.</returns>
|
||||
protected string DetermineVisualState()
|
||||
{
|
||||
if (!UsingLogicalPageNavigation())
|
||||
return "PrimaryView";
|
||||
|
||||
// Update the back button's enabled state when the view state changes
|
||||
var logicalPageBack = UsingLogicalPageNavigation() && ListView?.SelectedItem != null;
|
||||
|
||||
return logicalPageBack ? "SinglePane_Detail" : "SinglePane";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NavigationHelper registration
|
||||
|
||||
/// The methods provided in this section are simply used to allow
|
||||
/// NavigationHelper to respond to the page's navigation methods.
|
||||
///
|
||||
/// Page specific logic should be placed in event handlers for the
|
||||
/// <see cref="Common.NavigationHelper.LoadState"/>
|
||||
/// and <see cref="Common.NavigationHelper.SaveState"/>.
|
||||
/// The navigation parameter is available in the LoadState method
|
||||
/// in addition to page state preserved during an earlier session.
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
NavigationHelper.OnNavigatedTo(e);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
NavigationHelper.OnNavigatedFrom(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -6,19 +6,397 @@
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
xmlns:converters="using:ModernKeePass.Converters"
|
||||
xmlns:local="using:ModernKeePass.Controls"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:actions="using:ModernKeePass.Actions"
|
||||
x:Name="PageRoot"
|
||||
x:Class="ModernKeePass.Pages.EntryDetailPage"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
|
||||
</Page.Resources>
|
||||
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/>
|
||||
<converters:DoubleToForegroungBrushComplexityConverter x:Key="DoubleToForegroungBrushComplexityConverter"/>
|
||||
<Style TargetType="PasswordBox" x:Name="PasswordBoxWithButtonStyle">
|
||||
<Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}" />
|
||||
<Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource TextBoxForegroundThemeBrush}" />
|
||||
<Setter Property="Background" Value="{ThemeResource TextBoxBackgroundThemeBrush}" />
|
||||
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextSelectionHighlightColorThemeBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource TextBoxBorderThemeBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
|
||||
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="PasswordBox">
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<Style x:Name="RevealButtonStyle" TargetType="Button">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"
|
||||
Duration="0" />
|
||||
<DoubleAnimation Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"
|
||||
Duration="0" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Border x:Name="BorderElement"
|
||||
BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"/>
|
||||
<Border x:Name="BackgroundElement"
|
||||
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}"
|
||||
Margin="{TemplateBinding BorderThickness}">
|
||||
<TextBlock x:Name="GlyphElement"
|
||||
Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
FontStyle="Normal"
|
||||
Text=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Name="GeneratorButtonStyle" TargetType="Button">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" >
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"
|
||||
Duration="0" />
|
||||
<DoubleAnimation Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"
|
||||
Duration="0" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Border x:Name="BorderElement"
|
||||
BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"/>
|
||||
<Border x:Name="BackgroundElement"
|
||||
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}"
|
||||
Margin="{TemplateBinding BorderThickness}">
|
||||
<TextBlock x:Name="GlyphElement"
|
||||
Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
FontStyle="Normal"
|
||||
Text=""
|
||||
Padding="4,0,4,0"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBorderThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Normal">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="{ThemeResource TextControlBackgroundThemeOpacity}" />
|
||||
<DoubleAnimation Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="{ThemeResource TextControlBorderThemeOpacity}" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="{ThemeResource TextControlPointerOverBackgroundThemeOpacity}" />
|
||||
<DoubleAnimation Storyboard.TargetName="BorderElement"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="{ThemeResource TextControlPointerOverBorderThemeOpacity}" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Focused" />
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ButtonStates">
|
||||
<VisualState x:Name="ButtonVisible">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RevealButton"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="ButtonCollapsed" />
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Border x:Name="BackgroundElement"
|
||||
Grid.Row="1"
|
||||
Background="{TemplateBinding Background}"
|
||||
Margin="{TemplateBinding BorderThickness}"
|
||||
Grid.ColumnSpan="3"
|
||||
Grid.RowSpan="1"/>
|
||||
<Border x:Name="BorderElement"
|
||||
Grid.Row="1"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Grid.ColumnSpan="3"
|
||||
Grid.RowSpan="1"/>
|
||||
<ContentPresenter x:Name="HeaderContentPresenter"
|
||||
Grid.Row="0"
|
||||
Foreground="{ThemeResource TextBoxForegroundHeaderThemeBrush}"
|
||||
Margin="0,4,0,4"
|
||||
Grid.ColumnSpan="3"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
FontWeight="Semilight" />
|
||||
<ScrollViewer x:Name="ContentElement"
|
||||
Grid.Row="1"
|
||||
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
|
||||
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
|
||||
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
|
||||
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
|
||||
Margin="{TemplateBinding BorderThickness}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
IsTabStop="False"
|
||||
ZoomMode="Disabled"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
<ContentControl x:Name="PlaceholderTextContentPresenter"
|
||||
Grid.Row="1"
|
||||
Foreground="{ThemeResource TextBoxPlaceholderTextThemeBrush}"
|
||||
Margin="{TemplateBinding BorderThickness}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
IsTabStop="False"
|
||||
Grid.ColumnSpan="3"
|
||||
Content="{TemplateBinding PlaceholderText}"
|
||||
IsHitTestVisible="False"/>
|
||||
<Button x:Name="RevealButton"
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource RevealButtonStyle}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
IsTabStop="False"
|
||||
Grid.Column="1"
|
||||
Visibility="Collapsed"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
VerticalAlignment="Stretch"/>
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource GeneratorButtonStyle}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
IsTabStop="False"
|
||||
Grid.Column="2"
|
||||
Visibility="Visible"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
VerticalAlignment="Stretch" >
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:DataTriggerBehavior Binding="{Binding Password}" ComparisonCondition="NotEqual" Value="" >
|
||||
<!--<actions:CloseFlyoutAction />-->
|
||||
<core:CallMethodAction MethodName="Hide" />
|
||||
</core:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<StackPanel>
|
||||
<TextBlock>
|
||||
<Run Text="Password Length: "/>
|
||||
<Run Text="{Binding PasswordLength}" />
|
||||
</TextBlock>
|
||||
<Slider Value="{Binding PasswordLength, Mode=TwoWay}" Margin="0,-10,0,-20" />
|
||||
<CheckBox IsChecked="{Binding UpperCasePatternSelected, Mode=TwoWay}" Content="Upper case (A, B, C, ...)"/>
|
||||
<CheckBox IsChecked="{Binding LowerCasePatternSelected, Mode=TwoWay}" Content="Lower case (a, b, c, ...)"/>
|
||||
<CheckBox IsChecked="{Binding DigitsPatternSelected, Mode=TwoWay}" Content="Digits (0, 1, 2, ...)"/>
|
||||
<CheckBox IsChecked="{Binding MinusPatternSelected, Mode=TwoWay}" Content="Minus (-)"/>
|
||||
<CheckBox IsChecked="{Binding UnderscorePatternSelected, Mode=TwoWay}" Content="Underscore (_)"/>
|
||||
<CheckBox IsChecked="{Binding SpacePatternSelected, Mode=TwoWay}" Content="Space ( )"/>
|
||||
<CheckBox IsChecked="{Binding SpecialPatternSelected, Mode=TwoWay}" Content="Special (!, $, %, ...)"/>
|
||||
<CheckBox IsChecked="{Binding BracketsPatternSelected, Mode=TwoWay}" Content="Brackets ([], {}, (), ...)"/>
|
||||
<Button Content="Generate">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:EventTriggerBehavior EventName="Click">
|
||||
<core:CallMethodAction TargetObject="{Binding}" MethodName="GeneratePassword"/>
|
||||
</core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Page.Resources>
|
||||
<Page.DataContext>
|
||||
<viewModels:EntryVm/>
|
||||
</Page.DataContext>
|
||||
|
||||
<Page.BottomAppBar>
|
||||
<CommandBar x:Name="CommandBar" VerticalAlignment="Center">
|
||||
<CommandBar.SecondaryCommands>
|
||||
<AppBarButton Icon="Save" Label="Save">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:EventTriggerBehavior EventName="Click">
|
||||
<core:CallMethodAction TargetObject="{Binding}" MethodName="Save"/>
|
||||
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
|
||||
</core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</AppBarButton>
|
||||
<!--<AppBarButton Icon="Setting" Label="Settings">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:EventTriggerBehavior EventName="Click">
|
||||
<core:NavigateToPageAction TargetPage="ModernKeePass.Pages.SettingsPage" />
|
||||
</core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</AppBarButton>-->
|
||||
</CommandBar.SecondaryCommands>
|
||||
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:EventTriggerBehavior EventName="Click">
|
||||
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
|
||||
</core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</AppBarToggleButton>
|
||||
<AppBarButton Icon="Delete" Label="Delete" Click="DeleteButton_Click" />
|
||||
</CommandBar>
|
||||
</Page.BottomAppBar>
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.ChildrenTransitions>
|
||||
<TransitionCollection>
|
||||
@@ -26,60 +404,80 @@
|
||||
</TransitionCollection>
|
||||
</Grid.ChildrenTransitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="140"/>
|
||||
<RowDefinition Height="40"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="1" Margin="20,0,0,0" >
|
||||
<StackPanel x:Name="stackPanel" Grid.Row="1" Margin="20,0,0,0" >
|
||||
<StackPanel.Resources>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Margin" Value="0,20,0,0"/>
|
||||
</Style>
|
||||
<Style TargetType="CheckBox">
|
||||
<Setter Property="Margin" Value="0,20,0,0"/>
|
||||
</Style>
|
||||
</StackPanel.Resources>
|
||||
<TextBlock x:Name="userTextBlock" TextWrapping="Wrap" Text="User name or login" FontSize="18"/>
|
||||
<TextBox x:Name="userTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding UserName, Mode=TwoWay}" Width="350" Height="32" />
|
||||
<TextBlock x:Name="passwordTextBlock" TextWrapping="Wrap" Text="Password" FontSize="18"/>
|
||||
<PasswordBox x:Name="passwordBox" HorizontalAlignment="Left" Password="{Binding Password, Mode=TwoWay}" Width="350" Height="32" IsPasswordRevealButtonEnabled="True" Visibility="{Binding IsRevealPassword, Converter={StaticResource InverseBooleanToVisibilityConverter}}" />
|
||||
<TextBox x:Name="passwordTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Password, Mode=TwoWay}" Width="350" Height="32" Visibility="{Binding IsRevealPassword, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<CheckBox x:Name="checkBox" HorizontalAlignment="Left" Margin="-3,0,0,0" Content="Show password" IsChecked="{Binding IsRevealPassword, Mode=TwoWay}"/>
|
||||
<TextBlock x:Name="urlTextBlock" TextWrapping="Wrap" Text="URL" FontSize="18"/>
|
||||
<!--<StackPanel Orientation="Horizontal" Margin="0,-1,0,-1" Width="350" HorizontalAlignment="Left">
|
||||
<TextBox x:Name="urlTextBox" TextWrapping="Wrap" Text="{Binding Url, Mode=TwoWay}" Height="32" Width="350" MaxLength="256" Padding="0,0,34,0" />
|
||||
<Button Click="UrlButton_Click" Height="34" Margin="-34,0,0,0" Background="Transparent" Padding="2,0" >
|
||||
<SymbolIcon Symbol="Forward" VerticalAlignment="Center" />
|
||||
</Button>
|
||||
</StackPanel>-->
|
||||
<local:TextBoxWithButton x:Name="urlTextBox" HorizontalAlignment="Left" Text="{Binding Url, Mode=TwoWay}" Height="32" Width="350" MaxLength="256" Style="{StaticResource TextBoxWithButtonStyle}" GotoClick="UrlButton_Click" />
|
||||
<TextBlock x:Name="notesTextBlock" TextWrapping="Wrap" Text="Notes" FontSize="18"/>
|
||||
<TextBox x:Name="notesTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Notes, Mode=TwoWay}" Width="350" Height="200" AcceptsReturn="True" IsSpellCheckEnabled="True" />
|
||||
<TextBlock TextWrapping="Wrap" Text="User name or login" FontSize="18"/>
|
||||
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding UserName, Mode=TwoWay}" Width="350" Height="32" />
|
||||
<TextBlock TextWrapping="Wrap" Text="Password" FontSize="18"/>
|
||||
<PasswordBox HorizontalAlignment="Left" Password="{Binding Password, Mode=TwoWay}" Width="350" Height="32" IsPasswordRevealButtonEnabled="True" Visibility="{Binding IsRevealPassword, Converter={StaticResource InverseBooleanToVisibilityConverter}}" Style="{StaticResource PasswordBoxWithButtonStyle}" />
|
||||
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Password, Mode=TwoWay}" Width="350" Height="32" Visibility="{Binding IsRevealPassword, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<ProgressBar Value="{Binding PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}}" Maximum="128" Width="350" HorizontalAlignment="Left" Foreground="{Binding PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroungBrushComplexityConverter}}" />
|
||||
<CheckBox HorizontalAlignment="Left" Margin="-3,0,0,0" Content="Show password" IsChecked="{Binding IsRevealPassword, Mode=TwoWay}" IsEnabled="{Binding IsRevealPasswordEnabled}" />
|
||||
<TextBlock TextWrapping="Wrap" Text="URL" FontSize="18"/>
|
||||
<local:TextBoxWithButton x:Name="UrlTextBox" HorizontalAlignment="Left" Text="{Binding Url, Mode=TwoWay}" Height="32" Width="350" MaxLength="256" Style="{StaticResource TextBoxWithButtonStyle}" GotoClick="UrlButton_Click" />
|
||||
<TextBlock TextWrapping="Wrap" Text="Notes" FontSize="18"/>
|
||||
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Notes, Mode=TwoWay}" Width="350" Height="200" AcceptsReturn="True" IsSpellCheckEnabled="True" />
|
||||
<CheckBox FontSize="18" IsChecked="{Binding HasExpirationDate, Mode=TwoWay}" Content="Expiration date"/>
|
||||
<StackPanel Orientation="Horizontal" IsHitTestVisible="{Binding HasExpirationDate}">
|
||||
<SymbolIcon Symbol="Important" Foreground="DarkRed" Visibility="{Binding HasExpired, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="Password has expired" />
|
||||
</ToolTipService.ToolTip>
|
||||
</SymbolIcon>
|
||||
<DatePicker Margin="0,0,20,0" Date="{Binding ExpiryDate, Mode=TwoWay}" />
|
||||
<TimePicker Time="{Binding ExpiryTime, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Bouton Précédent et titre de la page -->
|
||||
<Grid>
|
||||
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="60"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
<ColumnDefinition Width="400"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Margin="39,0,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
|
||||
Style="{StaticResource NavigationBackButtonNormalStyle}"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="Back"
|
||||
AutomationProperties.AutomationId="BackButton"
|
||||
AutomationProperties.ItemType="Navigation Button"/>
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
Text="{Binding Title, Mode=TwoWay}"
|
||||
Style="{StaticResource HeaderTextBoxStyle}"
|
||||
<Button Grid.Column="0"
|
||||
x:Name="BackButton"
|
||||
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
|
||||
Height="40"
|
||||
Width="50"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="Back"
|
||||
AutomationProperties.AutomationId="BackButton"
|
||||
AutomationProperties.ItemType="Navigation Button"
|
||||
Style="{StaticResource NoBorderButtonStyle}">
|
||||
<SymbolIcon Symbol="Back" />
|
||||
</Button>
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
x:Name="TitleTextBox"
|
||||
Text="{Binding Name, Mode=TwoWay}"
|
||||
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
|
||||
Background="Transparent"
|
||||
IsHitTestVisible="{Binding IsEditMode}"
|
||||
BorderThickness="0"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
TextWrapping="NoWrap"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,30,0"/>
|
||||
<CommandBar Grid.Column="2" Background="Transparent" IsOpen="True" VerticalAlignment="Center" Margin="0,20,0,0">
|
||||
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}" />
|
||||
<AppBarButton Icon="Delete" Label="Delete" Click="AppBarButton_Click" />
|
||||
</CommandBar>
|
||||
PlaceholderText="New group name...">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="True">
|
||||
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
|
||||
</core:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</TextBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
</Page>
|
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Popups;
|
||||
using ModernKeePass.Common;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using ModernKeePass.Common;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
// Pour en savoir plus sur le modèle d'élément Page Détail de l'élément, consultez la page http://go.microsoft.com/fwlink/?LinkId=234232
|
||||
@@ -14,9 +16,11 @@ namespace ModernKeePass.Pages
|
||||
/// Page affichant les détails d'un élément au sein d'un groupe, offrant la possibilité de
|
||||
/// consulter les autres éléments qui appartiennent au même groupe.
|
||||
/// </summary>
|
||||
public sealed partial class EntryDetailPage : Page
|
||||
public sealed partial class EntryDetailPage
|
||||
{
|
||||
private NavigationHelper navigationHelper;
|
||||
private NavigationHelper navigationHelper;
|
||||
|
||||
public EntryVm Model => (EntryVm) DataContext;
|
||||
|
||||
/// <summary>
|
||||
/// NavigationHelper est utilisé sur chaque page pour faciliter la navigation et
|
||||
@@ -58,10 +62,8 @@ namespace ModernKeePass.Pages
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
navigationHelper.OnNavigatedTo(e);
|
||||
if (e.Parameter is EntryVm)
|
||||
{
|
||||
DataContext = e.Parameter as EntryVm;
|
||||
}
|
||||
if (!(e.Parameter is EntryVm)) return;
|
||||
DataContext = (EntryVm)e.Parameter;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
@@ -71,35 +73,16 @@ namespace ModernKeePass.Pages
|
||||
|
||||
#endregion
|
||||
|
||||
private async void AppBarButton_Click(object sender, RoutedEventArgs e)
|
||||
private void DeleteButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Create the message dialog and set its content
|
||||
var messageDialog = new MessageDialog("Are you sure you want to delete this entry?");
|
||||
|
||||
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
|
||||
messageDialog.Commands.Add(new UICommand("Delete", delete =>
|
||||
{
|
||||
var entry = DataContext as EntryVm;
|
||||
entry?.RemoveEntry();
|
||||
if (Frame.CanGoBack) Frame.GoBack();
|
||||
}));
|
||||
messageDialog.Commands.Add(new UICommand("Cancel"));
|
||||
|
||||
// Set the command that will be invoked by default
|
||||
messageDialog.DefaultCommandIndex = 1;
|
||||
|
||||
// Set the command to be invoked when escape is pressed
|
||||
messageDialog.CancelCommandIndex = 1;
|
||||
|
||||
// Show the message dialog
|
||||
await messageDialog.ShowAsync();
|
||||
MessageDialogHelper.ShowDeleteConfirmationDialog("Are you sure you want to delete this entry?", Model, Frame);
|
||||
}
|
||||
|
||||
private async void UrlButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = new Uri(urlTextBox.Text);
|
||||
var uri = new Uri(UrlTextBox.Text);
|
||||
await Windows.System.Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
catch
|
||||
|
@@ -5,9 +5,10 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
xmlns:converters="using:ModernKeePass.Converters"
|
||||
xmlns:local="using:ModernKeePass.Controls"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:actions="using:ModernKeePass.Actions"
|
||||
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
|
||||
x:Name="PageRoot"
|
||||
x:Class="ModernKeePass.Pages.GroupDetailPage"
|
||||
mc:Ignorable="d" >
|
||||
@@ -15,12 +16,41 @@
|
||||
<SolidColorBrush x:Key="Transparent" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="SystemColor" Color="{StaticResource SystemColorButtonFaceColor}" />
|
||||
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
|
||||
<converters:PluralizationConverter x:Key="PluralizationConverter"/>
|
||||
<converters:TextToFontStyleConverter x:Key="TextToFontStyleConverter"/>
|
||||
</Page.Resources>
|
||||
<Page.DataContext>
|
||||
<viewModels:GroupVm />
|
||||
</Page.DataContext>
|
||||
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Page.BottomAppBar>
|
||||
<CommandBar x:Name="CommandBar" VerticalAlignment="Center">
|
||||
<CommandBar.SecondaryCommands>
|
||||
<AppBarButton Icon="Save" Label="Save">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:EventTriggerBehavior EventName="Click">
|
||||
<core:CallMethodAction TargetObject="{Binding}" MethodName="Save"/>
|
||||
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
|
||||
</core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</AppBarButton>
|
||||
<!--<AppBarButton Icon="Setting" Label="Settings">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:EventTriggerBehavior EventName="Click">
|
||||
<core:NavigateToPageAction TargetPage="ModernKeePass.Pages.SettingsPage" />
|
||||
</core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</AppBarButton>-->
|
||||
</CommandBar.SecondaryCommands>
|
||||
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:EventTriggerBehavior EventName="Click">
|
||||
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
|
||||
</core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</AppBarToggleButton>
|
||||
<AppBarButton Icon="Delete" Label="Delete" IsEnabled="{Binding IsNotRoot}" Click="DeleteButton_Click" />
|
||||
</CommandBar>
|
||||
</Page.BottomAppBar>
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<CollectionViewSource
|
||||
x:Name="GroupsViewSource"
|
||||
@@ -32,26 +62,94 @@
|
||||
x:Name="EntriesZoomedOutViewSource"
|
||||
Source="{Binding EntriesZoomedOut}" IsSourceGrouped="True"/>
|
||||
</Grid.Resources>
|
||||
<Grid.Background>
|
||||
<StaticResource ResourceKey="ApplicationPageBackgroundThemeBrush"/>
|
||||
</Grid.Background>
|
||||
<Grid.ChildrenTransitions>
|
||||
<TransitionCollection>
|
||||
<EntranceThemeTransition/>
|
||||
</TransitionCollection>
|
||||
</Grid.ChildrenTransitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="140"/>
|
||||
<RowDefinition Height="40"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ListView
|
||||
Grid.Column="0"
|
||||
x:Name="LeftListView"
|
||||
Margin="0,0,0,-10"
|
||||
SelectionChanged="groups_SelectionChanged"
|
||||
IsSwipeEnabled="false"
|
||||
IsSynchronizedWithCurrentItem="False"
|
||||
DataContext="{Binding DataContext, ElementName=PageRoot}"
|
||||
RequestedTheme="Dark"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}">
|
||||
<ListView.Resources>
|
||||
<DataTemplate x:Name="Collapsed">
|
||||
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{Binding Name}" />
|
||||
</ToolTipService.ToolTip>
|
||||
</SymbolIcon>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Name="Expanded">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0" />
|
||||
<TextBlock Text="{Binding Name}" FontWeight="{Binding FontWeight}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="{Binding Name, ConverterParameter=Recycle Bin, Converter={StaticResource TextToFontStyleConverter}}" />
|
||||
<!--<TextBlock Text="{Binding EntryCount}" HorizontalAlignment="Right" VerticalAlignment="Center" />-->
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.Resources>
|
||||
<ListView.ItemsSource>
|
||||
<Binding Source="{StaticResource GroupsViewSource}"/>
|
||||
</ListView.ItemsSource>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
<!--<Setter Property="HorizontalContentAlignment" Value="Center" />-->
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<ToggleButton Style="{StaticResource HamburgerToggleButton}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:EventTriggerBehavior EventName="Loaded">
|
||||
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Collapsed}" TargetObject="{Binding ElementName=LeftListView}"/>
|
||||
</core:EventTriggerBehavior>
|
||||
<core:EventTriggerBehavior EventName="Checked">
|
||||
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Expanded}" TargetObject="{Binding ElementName=LeftListView}"/>
|
||||
</core:EventTriggerBehavior>
|
||||
<core:EventTriggerBehavior EventName="Unchecked">
|
||||
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Collapsed}" TargetObject="{Binding ElementName=LeftListView}"/>
|
||||
</core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ToggleButton>
|
||||
</DataTemplate>
|
||||
</ListView.HeaderTemplate>
|
||||
</ListView>
|
||||
<!-- Horizontal scrolling grid -->
|
||||
<SemanticZoom Grid.Row="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" >
|
||||
<SemanticZoom Grid.Column="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" Margin="0,30,0,0">
|
||||
<SemanticZoom.ZoomedInView>
|
||||
<GridView
|
||||
<GridView
|
||||
x:Name="GridView"
|
||||
AutomationProperties.AutomationId="ItemGridView"
|
||||
AutomationProperties.Name="Entries"
|
||||
TabIndex="1"
|
||||
IsSwipeEnabled="false"
|
||||
SelectionChanged="entries_SelectionChanged"
|
||||
IsSynchronizedWithCurrentItem="False">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="False">
|
||||
<actions:SetupFocusAction TargetObject="{Binding ElementName=GridView}" />
|
||||
</core:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<GridView.Resources>
|
||||
<DataTemplate x:Name="GroupFirstItem">
|
||||
<Border
|
||||
@@ -63,17 +161,17 @@
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" >
|
||||
<SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" Foreground="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}" >
|
||||
<SymbolIcon.RenderTransform>
|
||||
<CompositeTransform ScaleX="2" TranslateX="0" TranslateY="0" ScaleY="2"/>
|
||||
</SymbolIcon.RenderTransform>
|
||||
</SymbolIcon>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Title}" FontWeight="Bold" Style="{ThemeResource TitleTextBlockStyle}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="13,0,0,5"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Name}" FontWeight="Bold" Style="{ThemeResource TitleTextBlockStyle}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="13,0,0,5"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Name="GroupOtherItem">
|
||||
<Grid Height="110" Width="480" >
|
||||
<Grid Height="110" Width="480" x:Name="EntryGrid" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
@@ -90,14 +188,15 @@
|
||||
</SymbolIcon>
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,10,0,0" >
|
||||
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}"/>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
|
||||
<!--<TextBlock Text="{Binding EntryCount, ConverterParameter=entry\,entries, Converter={StaticResource PluralizationConverter}}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
|
||||
<TextBlock Text="{Binding GroupCount, ConverterParameter=group\,groups, Converter={StaticResource PluralizationConverter}}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />-->
|
||||
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
|
||||
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<TextBlock x:Name="NameTextBlock" Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}"/>
|
||||
<!--<Line Visibility="{Binding HasExpired, Converter={StaticResource BooleanToVisibilityConverter}}" Margin="0,-10,0,0" Stretch="Fill" Stroke="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}" StrokeThickness="1" X1="1" Width="{Binding Name, Converter={StaticResource TextToWidthConverter}, ConverterParameter=7}" HorizontalAlignment="Left" VerticalAlignment="Center" />-->
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
|
||||
<!--<TextBlock Text="{Binding EntryCount, ConverterParameter=entry\,entries, Converter={StaticResource PluralizationConverter}}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
|
||||
<TextBlock Text="{Binding GroupCount, ConverterParameter=group\,groups, Converter={StaticResource PluralizationConverter}}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />-->
|
||||
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
|
||||
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GridView.Resources>
|
||||
<GridView.ItemsSource>
|
||||
@@ -107,7 +206,7 @@
|
||||
<viewModels:EntryVm/>
|
||||
</GridView.DataContext>
|
||||
<GridView.ItemTemplateSelector>
|
||||
<local:FirstItemDataTemplateSelector
|
||||
<templateSelectors:FirstItemDataTemplateSelector
|
||||
FirstItem="{StaticResource GroupFirstItem}"
|
||||
OtherItem="{StaticResource GroupOtherItem}" />
|
||||
</GridView.ItemTemplateSelector>
|
||||
@@ -116,57 +215,6 @@
|
||||
<Setter Property="Margin" Value="52,0,0,2"/>
|
||||
</Style>
|
||||
</GridView.ItemContainerStyle>
|
||||
<GridView.Header>
|
||||
<ListView
|
||||
x:Name="LeftListView"
|
||||
SelectionChanged="groups_SelectionChanged"
|
||||
IsSwipeEnabled="false"
|
||||
IsSynchronizedWithCurrentItem="False"
|
||||
DataContext="{Binding DataContext, ElementName=PageRoot}"
|
||||
RequestedTheme="Dark"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}">
|
||||
<ListView.Resources>
|
||||
<DataTemplate x:Name="Collapsed">
|
||||
<SymbolIcon Symbol="{Binding IconSymbol}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{Binding Name}" />
|
||||
</ToolTipService.ToolTip>
|
||||
</SymbolIcon>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Name="Expanded">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<SymbolIcon Symbol="{Binding IconSymbol}" />
|
||||
<TextBlock Text="{Binding Name}" FontWeight="{Binding FontWeight}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" />
|
||||
<!--<TextBlock Text="{Binding EntryCount}" HorizontalAlignment="Right" VerticalAlignment="Center" />-->
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.Resources>
|
||||
<ListView.ItemsSource>
|
||||
<Binding Source="{StaticResource GroupsViewSource}"/>
|
||||
</ListView.ItemsSource>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Padding" Value="20,0,0,0"/>
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<ToggleButton IsChecked="{Binding IsLeftPaneOpen, Mode=TwoWay}" Style="{StaticResource HamburgerToggleButton}" Margin="0" />
|
||||
</DataTemplate>
|
||||
</ListView.HeaderTemplate>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:DataTriggerBehavior Binding="{Binding IsLeftPaneOpen}" Value="True">
|
||||
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Expanded}"/>
|
||||
</core:DataTriggerBehavior>
|
||||
<core:DataTriggerBehavior Binding="{Binding IsLeftPaneOpen}" Value="False">
|
||||
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Collapsed}"/>
|
||||
</core:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ListView>
|
||||
</GridView.Header>
|
||||
</GridView>
|
||||
</SemanticZoom.ZoomedInView>
|
||||
|
||||
@@ -185,7 +233,7 @@
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
|
||||
<TextBlock Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
@@ -194,7 +242,7 @@
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Background="LightGray" Margin="0,0,10,0" HorizontalAlignment="Left">
|
||||
<TextBlock Text="{Binding Key}" Foreground="Black" Margin="30" Style="{StaticResource HeaderTextBlockStyle}"/>
|
||||
<TextBlock Text="{Binding Key}" Width="40" Foreground="Black" Margin="30" Style="{StaticResource HeaderTextBlockStyle}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
@@ -204,40 +252,46 @@
|
||||
</SemanticZoom.ZoomedOutView>
|
||||
</SemanticZoom>
|
||||
|
||||
</Grid>
|
||||
<!-- Back button and page title -->
|
||||
<Grid Grid.Row="0">
|
||||
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="60"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="400"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Margin="39,0,39,0"
|
||||
<Button Grid.Column="0"
|
||||
x:Name="BackButton"
|
||||
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
|
||||
Style="{StaticResource NavigationBackButtonNormalStyle}"
|
||||
Height="40"
|
||||
Width="50"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="Back"
|
||||
AutomationProperties.AutomationId="BackButton"
|
||||
AutomationProperties.ItemType="Navigation Button"/>
|
||||
AutomationProperties.ItemType="Navigation Button"
|
||||
Style="{StaticResource NoBorderButtonStyle}">
|
||||
<SymbolIcon Symbol="Back" />
|
||||
</Button>
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
x:Name="TitleTextBox"
|
||||
Text="{Binding Name, Mode=TwoWay}"
|
||||
Style="{StaticResource HeaderTextBoxStyle}"
|
||||
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
|
||||
Background="Transparent"
|
||||
IsHitTestVisible="{Binding IsEditMode}"
|
||||
BorderThickness="0"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
TextWrapping="NoWrap"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,30,0"/>
|
||||
<CommandBar Grid.Column="2" Background="Transparent" IsOpen="True" VerticalAlignment="Center" Margin="0,20,0,0">
|
||||
<AppBarButton Icon="Find" Label="Search">
|
||||
<AppBarButton.Flyout>
|
||||
<Flyout>
|
||||
<SearchBox PlaceholderText="Search..." Width="350" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" />
|
||||
</Flyout>
|
||||
</AppBarButton.Flyout>
|
||||
</AppBarButton>
|
||||
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}" />
|
||||
<AppBarButton Icon="Delete" Label="Delete" IsEnabled="{Binding IsNotRoot}" Click="DeleteButton_Click" />
|
||||
</CommandBar>
|
||||
PlaceholderText="New group name...">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="True">
|
||||
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
|
||||
</core:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</TextBox>
|
||||
<SearchBox Grid.Column="2" PlaceholderText="Search..." Width="350" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
@@ -1,10 +1,14 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Popups;
|
||||
using ModernKeePass.Common;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using ModernKeePass.Common;
|
||||
using ModernKeePass.Events;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
// The Group Detail Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234229
|
||||
@@ -15,14 +19,15 @@ namespace ModernKeePass.Pages
|
||||
/// A page that displays an overview of a single group, including a preview of the items
|
||||
/// within the group.
|
||||
/// </summary>
|
||||
public sealed partial class GroupDetailPage : Page
|
||||
public sealed partial class GroupDetailPage
|
||||
{
|
||||
/// <summary>
|
||||
/// NavigationHelper is used on each page to aid in navigation and
|
||||
/// process lifetime management
|
||||
/// </summary>
|
||||
public NavigationHelper NavigationHelper { get; }
|
||||
|
||||
public GroupVm Model => (GroupVm)DataContext;
|
||||
|
||||
public GroupDetailPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -58,8 +63,10 @@ namespace ModernKeePass.Pages
|
||||
{
|
||||
NavigationHelper.OnNavigatedTo(e);
|
||||
|
||||
if (!(e.Parameter is GroupVm)) return;
|
||||
DataContext = (GroupVm) e.Parameter;
|
||||
if (e.Parameter is PasswordEventArgs)
|
||||
DataContext = ((PasswordEventArgs) e.Parameter).RootGroup;
|
||||
else if (e.Parameter is GroupVm)
|
||||
DataContext = (GroupVm) e.Parameter;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
@@ -74,61 +81,46 @@ namespace ModernKeePass.Pages
|
||||
|
||||
private void groups_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (LeftListView.SelectedIndex == 0)
|
||||
GroupVm group;
|
||||
switch (LeftListView.SelectedIndex)
|
||||
{
|
||||
var currentGroup = DataContext as GroupVm;
|
||||
currentGroup?.CreateNewGroup();
|
||||
LeftListView.SelectedIndex = -1;
|
||||
// TODO: Navigate to new group?
|
||||
return;
|
||||
case -1:
|
||||
return;
|
||||
case 0:
|
||||
group = Model.CreateNewGroup();
|
||||
break;
|
||||
default:
|
||||
group = LeftListView.SelectedItem as GroupVm;
|
||||
break;
|
||||
}
|
||||
var selectedItem = LeftListView.SelectedItem as GroupVm;
|
||||
if (selectedItem == null) return;
|
||||
Frame.Navigate(typeof(GroupDetailPage), selectedItem);
|
||||
Frame.Navigate(typeof(GroupDetailPage), group);
|
||||
}
|
||||
|
||||
private void entries_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
EntryVm entry;
|
||||
switch (GridView.SelectedIndex)
|
||||
{
|
||||
case -1:
|
||||
return;
|
||||
case 0:
|
||||
var currentGroup = DataContext as GroupVm;
|
||||
currentGroup?.CreateNewEntry();
|
||||
GridView.SelectedIndex = -1;
|
||||
// TODO: Navigate to new entry?
|
||||
return;
|
||||
entry = Model.CreateNewEntry();
|
||||
break;
|
||||
default:
|
||||
entry = GridView.SelectedItem as EntryVm;
|
||||
break;
|
||||
}
|
||||
Frame.Navigate(typeof(EntryDetailPage), GridView.SelectedItem as EntryVm);
|
||||
Frame.Navigate(typeof(EntryDetailPage), entry);
|
||||
}
|
||||
|
||||
private async void DeleteButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
private void DeleteButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Create the message dialog and set its content
|
||||
var messageDialog = new MessageDialog("Are you sure you want to delete the whole group and all its entries?");
|
||||
|
||||
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
|
||||
messageDialog.Commands.Add(new UICommand("Delete", delete =>
|
||||
{
|
||||
var group = DataContext as GroupVm;
|
||||
group?.RemoveGroup();
|
||||
if (Frame.CanGoBack) Frame.GoBack();
|
||||
}));
|
||||
messageDialog.Commands.Add(new UICommand("Cancel"));
|
||||
|
||||
// Set the command that will be invoked by default
|
||||
messageDialog.DefaultCommandIndex = 1;
|
||||
|
||||
// Set the command to be invoked when escape is pressed
|
||||
messageDialog.CancelCommandIndex = 1;
|
||||
|
||||
// Show the message dialog
|
||||
await messageDialog.ShowAsync();
|
||||
MessageDialogHelper.ShowDeleteConfirmationDialog("Are you sure you want to delete the whole group and all its entries?", Model, Frame);
|
||||
}
|
||||
|
||||
private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
|
||||
{
|
||||
// We need to synchronize the two lists (zoomed-in and zoomed-out) because the source is different
|
||||
if (e.IsSourceZoomedInView == false)
|
||||
{
|
||||
e.DestinationItem.Item = e.SourceItem.Item;
|
||||
@@ -137,23 +129,20 @@ namespace ModernKeePass.Pages
|
||||
|
||||
private void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
|
||||
{
|
||||
var viewModel = DataContext as GroupVm;
|
||||
var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx://Assets/Logo.scale-80.png"));
|
||||
var results = viewModel.Entries.Skip(1).Where(e => e.Title.IndexOf(args.QueryText, StringComparison.OrdinalIgnoreCase) >= 0).Take(5);
|
||||
var results = Model.Entries.Skip(1).Where(e => e.Name.IndexOf(args.QueryText, StringComparison.OrdinalIgnoreCase) >= 0).Take(5);
|
||||
foreach (var result in results)
|
||||
{
|
||||
args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Title, result.ParentGroup.Name, result.Id, imageUri, string.Empty);
|
||||
args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Name, result.ParentGroup.Name, result.Id, imageUri, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchBox_OnResultSuggestionChosen(SearchBox sender, SearchBoxResultSuggestionChosenEventArgs args)
|
||||
{
|
||||
var viewModel = DataContext as GroupVm;
|
||||
var entry = viewModel.Entries.Skip(1).FirstOrDefault(e => e.Id == args.Tag);
|
||||
var entry = Model.Entries.Skip(1).FirstOrDefault(e => e.Id == args.Tag);
|
||||
Frame.Navigate(typeof(EntryDetailPage), entry);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
159
ModernKeePass/Pages/MainPage.xaml
Normal file
159
ModernKeePass/Pages/MainPage.xaml
Normal file
@@ -0,0 +1,159 @@
|
||||
<basePages:LayoutAwarePageBase
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
xmlns:controls="using:ModernKeePass.Controls"
|
||||
xmlns:basePages="using:ModernKeePass.Pages.BasePages"
|
||||
x:Class="ModernKeePass.Pages.MainPage"
|
||||
x:Name="PageRoot"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<CollectionViewSource
|
||||
x:Name="MenuItemsSource"
|
||||
Source="{Binding MainMenuItems}"
|
||||
IsSourceGrouped="True" />
|
||||
</Page.Resources>
|
||||
|
||||
<Page.Background>
|
||||
<StaticResource ResourceKey="ApplicationPageBackgroundThemeBrush"/>
|
||||
</Page.Background>
|
||||
<Page.DataContext>
|
||||
<viewModels:MainVm />
|
||||
</Page.DataContext>
|
||||
|
||||
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.ChildrenTransitions>
|
||||
<TransitionCollection>
|
||||
<EntranceThemeTransition/>
|
||||
</TransitionCollection>
|
||||
</Grid.ChildrenTransitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="40"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="PrimaryColumn" Width="250" />
|
||||
<ColumnDefinition x:Name="SecondaryColumn" Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Back button and page title -->
|
||||
<Grid x:Name="TitlePanel" Background="{ThemeResource AppBarBackgroundThemeBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button x:Name="BackButton"
|
||||
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
|
||||
Visibility="Collapsed"
|
||||
Height="40"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="Back"
|
||||
AutomationProperties.AutomationId="BackButton"
|
||||
AutomationProperties.ItemType="Navigation Button"
|
||||
Style="{StaticResource NoBorderButtonStyle}">
|
||||
<SymbolIcon Symbol="Back" />
|
||||
</Button>
|
||||
<TextBlock x:Name="TitleTextBox" Text="{Binding Name}" Grid.Column="1" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<controls:ListViewWithDisable
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
x:Name="MenuListView"
|
||||
SelectionChanged="MenuListView_SelectionChanged"
|
||||
Background="{ThemeResource AppBarBackgroundThemeBrush}"
|
||||
ItemsSource="{Binding Source={StaticResource MenuItemsSource}}"
|
||||
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||
IsSynchronizedWithCurrentItem="False"
|
||||
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
|
||||
<controls:ListViewWithDisable.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<SymbolIcon Symbol="{Binding SymbolIcon}" />
|
||||
<TextBlock Text="{Binding Title}" Margin="10,5,0,0" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</controls:ListViewWithDisable.ItemTemplate>
|
||||
<controls:ListViewWithDisable.GroupStyle>
|
||||
<GroupStyle HidesIfEmpty="True">
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Background="DarkGray" Margin="20,0,0,0">
|
||||
<Border Height="1" Width="300" HorizontalAlignment="Stretch"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>
|
||||
</controls:ListViewWithDisable.GroupStyle>
|
||||
</controls:ListViewWithDisable>
|
||||
<TextBlock x:Name="PageTitleTextBlock" Grid.Column="1" Grid.Row="0" FontSize="24" VerticalAlignment="Center" Margin="10,0,0,0" >
|
||||
<Run Text="{Binding SelectedItem}" />
|
||||
</TextBlock>
|
||||
<Frame x:Name="MenuFrame" Grid.Column="1" Grid.Row="1" Margin="0,10,0,0" />
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<!-- Visual states reflect the application's view state -->
|
||||
<VisualStateGroup x:Name="ViewStates">
|
||||
<VisualState x:Name="PrimaryView" />
|
||||
<VisualState x:Name="SinglePane">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PrimaryColumn" Storyboard.TargetProperty="Width">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="*"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SecondaryColumn" Storyboard.TargetProperty="Width">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PageTitleTextBlock" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuListView" Storyboard.TargetProperty="Padding">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="120,0,90,60"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<!--
|
||||
When an item is selected and only one pane is shown the details display requires more extensive changes:
|
||||
* Hide the master list and the column it was in
|
||||
* Move item details down a row to make room for the title
|
||||
* Move the title directly above the details
|
||||
* Adjust padding for details
|
||||
-->
|
||||
<VisualState x:Name="SinglePane_Detail">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PrimaryColumn" Storyboard.TargetProperty="Width">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuListView" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="(Grid.Row)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="(Grid.RowSpan)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>-->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitlePanel" Storyboard.TargetProperty="(Grid.Column)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackButton" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleTextBox" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PageTitleTextBlock" Storyboard.TargetProperty="Margin">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="60,0,0,0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="Margin">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="10,0,0,0"/>
|
||||
</ObjectAnimationUsingKeyFrames>-->
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</basePages:LayoutAwarePageBase>
|
37
ModernKeePass/Pages/MainPage.xaml.cs
Normal file
37
ModernKeePass/Pages/MainPage.xaml.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace ModernKeePass.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainPage
|
||||
{
|
||||
public new MainVm Model => (MainVm)DataContext;
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
ListView = MenuListView;
|
||||
ListViewSource = MenuItemsSource;
|
||||
}
|
||||
|
||||
private void MenuListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
ListView_SelectionChanged(sender, e);
|
||||
var selectedItem = Model.SelectedItem as MainMenuItemVm;
|
||||
selectedItem?.Destination.Navigate(selectedItem.PageType, selectedItem.Parameter);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
DataContext = new MainVm(Frame, MenuFrame);
|
||||
if (Model.SelectedItem == null) MenuFrame.Navigate(typeof(WelcomePage));
|
||||
}
|
||||
}
|
||||
}
|
38
ModernKeePass/Pages/MainPageFrames/AboutPage.xaml
Normal file
38
ModernKeePass/Pages/MainPageFrames/AboutPage.xaml
Normal file
@@ -0,0 +1,38 @@
|
||||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
x:Class="ModernKeePass.Pages.AboutPage"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.DataContext>
|
||||
<viewModels:AboutVm/>
|
||||
</Page.DataContext>
|
||||
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="10,0,0,0">
|
||||
<Run Text="{Binding Name}"/>
|
||||
<Run Text="version"/>
|
||||
<Run Text="{Binding Version}" />
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
|
||||
<Run Text="A modern password manager for the Windows Store"/>
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
|
||||
<Run Text="Homepage: "/>
|
||||
<Hyperlink NavigateUri="https://github.com/wismna/ModernKeePass">
|
||||
<Run Text="https://github.com/wismna/ModernKeePass"/>
|
||||
</Hyperlink></TextBlock>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="10,0,0,0">
|
||||
<Run Text="Credits:"/>
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
|
||||
<Run Text="Dominik Reichl for the KeePass application and file format"/>
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
|
||||
<Run Text="ArtjomP for his PCL adapatation of the KeePass Library"/>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Page>
|
15
ModernKeePass/Pages/MainPageFrames/AboutPage.xaml.cs
Normal file
15
ModernKeePass/Pages/MainPageFrames/AboutPage.xaml.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace ModernKeePass.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class AboutPage
|
||||
{
|
||||
public AboutPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
39
ModernKeePass/Pages/MainPageFrames/NewDatabasePage.xaml
Normal file
39
ModernKeePass/Pages/MainPageFrames/NewDatabasePage.xaml
Normal file
@@ -0,0 +1,39 @@
|
||||
<Page
|
||||
x:Class="ModernKeePass.Pages.NewDatabasePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="using:ModernKeePass.Controls"
|
||||
xmlns:converters="using:ModernKeePass.Converters"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/>
|
||||
<converters:DoubleToForegroungBrushComplexityConverter x:Key="DoubleToForegroungBrushComplexityConverter"/>
|
||||
</Page.Resources>
|
||||
<Page.DataContext>
|
||||
<viewModels:NewVm />
|
||||
</Page.DataContext>
|
||||
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<HyperlinkButton Content="Create new..." Click="ButtonBase_OnClick" />
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">Create a new password database to the location of your chosing.</TextBlock>
|
||||
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="550" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<StackPanel Margin="25,0,25,0">
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
<local:OpenDatabaseUserControl Password="{Binding Password, Mode=TwoWay}" CreateNew="True" >
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<Core:EventTriggerBehavior EventName="ValidationChecked">
|
||||
<Core:NavigateToPageAction TargetPage="ModernKeePass.Pages.GroupDetailPage" />
|
||||
</Core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</local:OpenDatabaseUserControl>
|
||||
<TextBlock >Password complexity</TextBlock>
|
||||
<ProgressBar Value="{Binding PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}}" Maximum="128" Width="500" HorizontalAlignment="Left" Foreground="{Binding PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroungBrushComplexityConverter}}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Page>
|
@@ -14,10 +14,12 @@ namespace ModernKeePass.Pages
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class NewDatabasePage : Page
|
||||
public sealed partial class NewDatabasePage
|
||||
{
|
||||
private Frame _mainFrame;
|
||||
|
||||
public NewVm Model => (NewVm)DataContext;
|
||||
|
||||
public NewDatabasePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -40,13 +42,7 @@ namespace ModernKeePass.Pages
|
||||
|
||||
var file = await savePicker.PickSaveFileAsync();
|
||||
if (file == null) return;
|
||||
var viewModel = DataContext as OpenVm;
|
||||
viewModel.OpenFile(file);
|
||||
}
|
||||
|
||||
private void PasswordUserControl_PasswordChecked(object sender, PasswordEventArgs e)
|
||||
{
|
||||
_mainFrame.Navigate(typeof(GroupDetailPage), e.RootGroup);
|
||||
Model.OpenFile(file);
|
||||
}
|
||||
}
|
||||
}
|
37
ModernKeePass/Pages/MainPageFrames/OpenDatabasePage.xaml
Normal file
37
ModernKeePass/Pages/MainPageFrames/OpenDatabasePage.xaml
Normal file
@@ -0,0 +1,37 @@
|
||||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
xmlns:local="using:ModernKeePass.Controls"
|
||||
xmlns:converters="using:ModernKeePass.Converters"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
x:Class="ModernKeePass.Pages.OpenDatabasePage"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
</Page.Resources>
|
||||
<Page.DataContext>
|
||||
<viewModels:OpenVm/>
|
||||
</Page.DataContext>
|
||||
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<HyperlinkButton Content="Browse files..." Click="ButtonBase_OnClick" />
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">Open an existing password database from your PC.</TextBlock>
|
||||
<HyperlinkButton Content="From Url..." IsEnabled="False" />
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">Open an existing password database from an Internet location (not yet implemented).</TextBlock>
|
||||
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="550" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<StackPanel Margin="25,0,25,0">
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
<local:OpenDatabaseUserControl>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<Core:EventTriggerBehavior EventName="ValidationChecked">
|
||||
<Core:NavigateToPageAction TargetPage="ModernKeePass.Pages.GroupDetailPage" />
|
||||
</Core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</local:OpenDatabaseUserControl>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Page>
|
@@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using ModernKeePass.Common;
|
||||
using ModernKeePass.Events;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
@@ -16,10 +13,12 @@ namespace ModernKeePass.Pages
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class OpenDatabasePage : Page
|
||||
public sealed partial class OpenDatabasePage
|
||||
{
|
||||
private Frame _mainFrame;
|
||||
|
||||
public OpenVm Model => (OpenVm)DataContext;
|
||||
|
||||
public OpenDatabasePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -41,14 +40,10 @@ namespace ModernKeePass.Pages
|
||||
};
|
||||
picker.FileTypeFilter.Add(".kdbx");
|
||||
|
||||
var viewModel = DataContext as OpenVm;
|
||||
// Application now has read/write access to the picked file
|
||||
viewModel.OpenFile(await picker.PickSingleFileAsync());
|
||||
}
|
||||
|
||||
private void PasswordUserControl_PasswordChecked(object sender, PasswordEventArgs e)
|
||||
{
|
||||
_mainFrame.Navigate(typeof(GroupDetailPage), e.RootGroup);
|
||||
var file = await picker.PickSingleFileAsync();
|
||||
if (file == null) return;
|
||||
Model.OpenFile(file);
|
||||
}
|
||||
}
|
||||
}
|
47
ModernKeePass/Pages/MainPageFrames/RecentDatabasesPage.xaml
Normal file
47
ModernKeePass/Pages/MainPageFrames/RecentDatabasesPage.xaml
Normal file
@@ -0,0 +1,47 @@
|
||||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
xmlns:local="using:ModernKeePass.Controls"
|
||||
xmlns:converters="using:ModernKeePass.Converters"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
x:Class="ModernKeePass.Pages.RecentDatabasesPage"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
<CollectionViewSource x:Name="RecentItemsSource" Source="{Binding RecentItems}" />
|
||||
</Page.Resources>
|
||||
<Page.DataContext>
|
||||
<viewModels:RecentVm/>
|
||||
</Page.DataContext>
|
||||
<ListView Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
|
||||
ItemsSource="{Binding Source={StaticResource RecentItemsSource}}"
|
||||
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="10,0,10,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="0" Text="{Binding Name}" Padding="5,0,0,0" />
|
||||
<TextBlock Grid.Row="1" Text="{Binding Path}" Padding="5,0,0,0" FontSize="10" />
|
||||
<local:OpenDatabaseUserControl Grid.Row="2" x:Name="DatabaseUserControl" HorizontalAlignment="Stretch" MinWidth="400" Margin="0,10,0,0" Visibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" ValidationChecking="OpenDatabaseUserControl_OnValidationChecking" >
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<Core:EventTriggerBehavior EventName="ValidationChecked">
|
||||
<Core:NavigateToPageAction TargetPage="ModernKeePass.Pages.GroupDetailPage" />
|
||||
</Core:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</local:OpenDatabaseUserControl>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Page>
|
@@ -2,7 +2,6 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using ModernKeePass.Events;
|
||||
using ModernKeePass.ViewModels;
|
||||
|
||||
// Pour en savoir plus sur le modèle d'élément Page vierge, consultez la page http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
@@ -12,10 +11,12 @@ namespace ModernKeePass.Pages
|
||||
/// <summary>
|
||||
/// Une page vide peut être utilisée seule ou constituer une page de destination au sein d'un frame.
|
||||
/// </summary>
|
||||
public sealed partial class RecentDatabasesPage : Page
|
||||
public sealed partial class RecentDatabasesPage
|
||||
{
|
||||
private Frame _mainFrame;
|
||||
|
||||
public RecentVm Model => (RecentVm)DataContext;
|
||||
|
||||
public RecentDatabasesPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -27,17 +28,10 @@ namespace ModernKeePass.Pages
|
||||
_mainFrame = e.Parameter as Frame;
|
||||
}
|
||||
|
||||
private void PasswordUserControl_PasswordChecked(object sender, PasswordEventArgs e)
|
||||
{
|
||||
_mainFrame.Navigate(typeof(GroupDetailPage), e.RootGroup);
|
||||
}
|
||||
|
||||
private void OpenDatabaseUserControl_OnValidationChecking(object sender, EventArgs e)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
var viewModel = DataContext as RecentVm;
|
||||
var app = (App)Application.Current;
|
||||
app.Database.DatabaseFile = viewModel.SelectedItem.DatabaseFile;
|
||||
app.Database.DatabaseFile = Model.SelectedItem.DatabaseFile;
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,7 +11,9 @@
|
||||
</Page.DataContext>
|
||||
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<HyperlinkButton x:Name="SaveButton" Content="Save and close" Click="SaveButton_OnClick" VerticalAlignment="Top" IsEnabled="{Binding IsSaveEnabled}" />
|
||||
<HyperlinkButton x:Name="SaveAsButton" Content="Save as..." Click="SaveAsButton_OnClick" VerticalAlignment="Top" IsEnabled="{Binding IsSaveEnabled}" />
|
||||
<HyperlinkButton Content="Save and close" Click="SaveButton_OnClick" />
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">This will save and close the currently opened database.</TextBlock>
|
||||
<HyperlinkButton Content="Save as..." Click="SaveAsButton_OnClick" />
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">This will save the currently opened database as a new file and leave it open.</TextBlock>
|
||||
</StackPanel>
|
||||
</Page>
|
@@ -13,9 +13,10 @@ namespace ModernKeePass.Pages
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class SaveDatabasePage : Page
|
||||
public sealed partial class SaveDatabasePage
|
||||
{
|
||||
private Frame _mainFrame;
|
||||
public SaveVm Model => (SaveVm)DataContext;
|
||||
public SaveDatabasePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -29,8 +30,7 @@ namespace ModernKeePass.Pages
|
||||
|
||||
private void SaveButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var viewModel = DataContext as SaveVm;
|
||||
viewModel.Save();
|
||||
Model.Save();
|
||||
_mainFrame.Navigate(typeof(MainPage));
|
||||
}
|
||||
|
||||
@@ -45,8 +45,7 @@ namespace ModernKeePass.Pages
|
||||
|
||||
var file = await savePicker.PickSaveFileAsync();
|
||||
if (file == null) return;
|
||||
var viewModel = DataContext as SaveVm;
|
||||
viewModel.Save(file);
|
||||
Model.Save(file);
|
||||
|
||||
_mainFrame.Navigate(typeof(MainPage));
|
||||
}
|
19
ModernKeePass/Pages/MainPageFrames/WelcomePage.xaml
Normal file
19
ModernKeePass/Pages/MainPageFrames/WelcomePage.xaml
Normal file
@@ -0,0 +1,19 @@
|
||||
<Page
|
||||
x:Class="ModernKeePass.Pages.WelcomePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,10">
|
||||
<SymbolIcon Symbol="Back" Margin="0,7,40,0" />
|
||||
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}">Have an existing password database? Open it here.</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,6,0,10">
|
||||
<SymbolIcon Symbol="Back" Margin="0,7,40,0" />
|
||||
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}">Want to create a new password database? Do it here.</TextBlock>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Page>
|
30
ModernKeePass/Pages/MainPageFrames/WelcomePage.xaml.cs
Normal file
30
ModernKeePass/Pages/MainPageFrames/WelcomePage.xaml.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace ModernKeePass.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class WelcomePage : Page
|
||||
{
|
||||
public WelcomePage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
<Page
|
||||
x:Class="ModernKeePass.Pages.NewDatabasePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="using:ModernKeePass.Controls"
|
||||
xmlns:converters="using:ModernKeePass.Converters"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
</Page.Resources>
|
||||
<Page.DataContext>
|
||||
<viewModels:OpenVm />
|
||||
</Page.DataContext>
|
||||
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<HyperlinkButton Content="Create new..." Click="ButtonBase_OnClick" />
|
||||
<TextBlock TextWrapping="Wrap" Text="{Binding Name}" Height="auto" Width="auto" FontSize="16" Margin="10,7,0,6" />
|
||||
<controls:OpenDatabaseUserControl CreateNew="True" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}" ValidationChecked="PasswordUserControl_PasswordChecked" />
|
||||
</StackPanel>
|
||||
</Page>
|
@@ -1,24 +0,0 @@
|
||||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
xmlns:local="using:ModernKeePass.Controls"
|
||||
xmlns:converters="using:ModernKeePass.Converters"
|
||||
x:Class="ModernKeePass.Pages.OpenDatabasePage"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
</Page.Resources>
|
||||
<Page.DataContext>
|
||||
<viewModels:OpenVm/>
|
||||
</Page.DataContext>
|
||||
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<HyperlinkButton Content="Browse files..." Click="ButtonBase_OnClick" />
|
||||
<HyperlinkButton Content="From Url..." IsEnabled="False" />
|
||||
<TextBlock TextWrapping="Wrap" Text="{Binding Name}" Height="auto" Width="auto" FontSize="16" Margin="10,7,0,6" />
|
||||
<local:OpenDatabaseUserControl HorizontalAlignment="Left" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}" ValidationChecked="PasswordUserControl_PasswordChecked" />
|
||||
</StackPanel>
|
||||
</Page>
|
@@ -1,38 +0,0 @@
|
||||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:ModernKeePass.ViewModels"
|
||||
xmlns:local="using:ModernKeePass.Controls"
|
||||
xmlns:converters="using:ModernKeePass.Converters"
|
||||
x:Class="ModernKeePass.Pages.RecentDatabasesPage"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
<CollectionViewSource x:Name="RecentItemsSource" Source="{Binding RecentItems}" />
|
||||
</Page.Resources>
|
||||
<Page.DataContext>
|
||||
<viewModels:RecentVm/>
|
||||
</Page.DataContext>
|
||||
<ListView
|
||||
Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
|
||||
ItemsSource="{Binding Source={StaticResource RecentItemsSource}}"
|
||||
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Margin="10,0,10,0">
|
||||
<TextBlock Text="{Binding Name}" Width="350" Padding="5,0,0,0" />
|
||||
<TextBlock Text="{Binding Path}" Width="350" Padding="5,0,0,0" FontSize="10" />
|
||||
<local:OpenDatabaseUserControl Margin="0,10,0,0" Visibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" ValidationChecking="OpenDatabaseUserControl_OnValidationChecking" ValidationChecked="PasswordUserControl_PasswordChecked" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
</ListView>
|
||||
</Page>
|
181
ModernKeePass/Pages/SettingsPage.xaml
Normal file
181
ModernKeePass/Pages/SettingsPage.xaml
Normal file
@@ -0,0 +1,181 @@
|
||||
<Page
|
||||
x:Name="pageRoot"
|
||||
x:Class="ModernKeePass.Pages.SettingsPage"
|
||||
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:ModernKeePass.Pages"
|
||||
xmlns:common="using:ModernKeePass.Common"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<!-- Collection of items displayed by this page -->
|
||||
<CollectionViewSource
|
||||
x:Name="itemsViewSource"
|
||||
Source="{Binding Items}"/>
|
||||
</Page.Resources>
|
||||
|
||||
<!--
|
||||
This grid acts as a root panel for the page that defines two rows:
|
||||
* Row 0 contains the back button and page title
|
||||
* Row 1 contains the rest of the page layout
|
||||
-->
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.ChildrenTransitions>
|
||||
<TransitionCollection>
|
||||
<EntranceThemeTransition/>
|
||||
</TransitionCollection>
|
||||
</Grid.ChildrenTransitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="140"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="primaryColumn" Width="420"/>
|
||||
<ColumnDefinition x:Name="secondaryColumn" Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Back button and page title -->
|
||||
<Grid x:Name="titlePanel">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
|
||||
Style="{StaticResource NavigationBackButtonNormalStyle}"
|
||||
VerticalAlignment="Top"
|
||||
AutomationProperties.Name="Back"
|
||||
AutomationProperties.AutomationId="BackButton"
|
||||
AutomationProperties.ItemType="Navigation Button"/>
|
||||
<TextBlock x:Name="pageTitle" Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
|
||||
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,0,40"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Vertical scrolling item list -->
|
||||
<ListView
|
||||
x:Name="itemListView"
|
||||
AutomationProperties.AutomationId="ItemsListView"
|
||||
AutomationProperties.Name="Items"
|
||||
TabIndex="1"
|
||||
Grid.Row="1"
|
||||
Margin="-10,-10,0,0"
|
||||
Padding="120,0,0,60"
|
||||
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
|
||||
IsSwipeEnabled="False"
|
||||
SelectionChanged="ItemListView_SelectionChanged">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="60" Height="60">
|
||||
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Margin="10,0,0,0">
|
||||
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" MaxHeight="40"/>
|
||||
<TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="FrameworkElement">
|
||||
<Setter Property="Margin" Value="0,0,0,10"/>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
</ListView>
|
||||
|
||||
|
||||
<!-- Details for selected item -->
|
||||
<ScrollViewer
|
||||
x:Name="itemDetail"
|
||||
AutomationProperties.AutomationId="ItemDetailScrollViewer"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
Padding="60,0,66,0"
|
||||
DataContext="{Binding SelectedItem, ElementName=itemListView}"
|
||||
HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.HorizontalScrollMode="Disabled" ScrollViewer.VerticalScrollMode="Enabled"
|
||||
ScrollViewer.ZoomMode="Disabled">
|
||||
|
||||
<Grid x:Name="itemDetailGrid" Margin="0,60,0,50">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Row="1" Margin="0,0,20,0" Width="180" Height="180" Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
|
||||
<StackPanel x:Name="itemDetailTitlePanel" Grid.Row="1" Grid.Column="1">
|
||||
<TextBlock x:Name="itemTitle" Margin="0,-10,0,0" Text="{Binding Title}" Style="{StaticResource SubheaderTextBlockStyle}"/>
|
||||
<TextBlock x:Name="itemSubtitle" Margin="0,0,0,20" Text="{Binding Subtitle}" Style="{StaticResource SubtitleTextBlockStyle}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Row="2" Grid.ColumnSpan="2" Margin="0,20,0,0" Text="{Binding Content}" Style="{StaticResource BodyTextBlockStyle}"/>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
|
||||
<!-- Visual states reflect the application's view state -->
|
||||
<VisualStateGroup x:Name="ViewStates">
|
||||
<VisualState x:Name="PrimaryView" />
|
||||
<VisualState x:Name="SinglePane">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="*"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="secondaryColumn" Storyboard.TargetProperty="Width">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Padding">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="120,0,90,60"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<!--
|
||||
When an item is selected and only one pane is shown the details display requires more extensive changes:
|
||||
* Hide the master list and the column it was in
|
||||
* Move item details down a row to make room for the title
|
||||
* Move the title directly above the details
|
||||
* Adjust padding for details
|
||||
-->
|
||||
<VisualState x:Name="SinglePane_Detail">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.Row)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.RowSpan)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="titlePanel" Storyboard.TargetProperty="(Grid.Column)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailGrid" Storyboard.TargetProperty="Margin">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,60"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Padding">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="120,0,90,0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</Page>
|
255
ModernKeePass/Pages/SettingsPage.xaml.cs
Normal file
255
ModernKeePass/Pages/SettingsPage.xaml.cs
Normal file
@@ -0,0 +1,255 @@
|
||||
using ModernKeePass.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Windows.Input;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The Split Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234234
|
||||
|
||||
namespace ModernKeePass.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// A page that displays a group title, a list of items within the group, and details for
|
||||
/// the currently selected item.
|
||||
/// </summary>
|
||||
public sealed partial class SettingsPage : Page
|
||||
{
|
||||
private NavigationHelper navigationHelper;
|
||||
private ObservableDictionary defaultViewModel = new ObservableDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// This can be changed to a strongly typed view model.
|
||||
/// </summary>
|
||||
public ObservableDictionary DefaultViewModel
|
||||
{
|
||||
get { return this.defaultViewModel; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NavigationHelper is used on each page to aid in navigation and
|
||||
/// process lifetime management
|
||||
/// </summary>
|
||||
public NavigationHelper NavigationHelper
|
||||
{
|
||||
get { return this.navigationHelper; }
|
||||
}
|
||||
|
||||
public SettingsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Setup the navigation helper
|
||||
this.navigationHelper = new NavigationHelper(this);
|
||||
this.navigationHelper.LoadState += navigationHelper_LoadState;
|
||||
this.navigationHelper.SaveState += navigationHelper_SaveState;
|
||||
|
||||
// Setup the logical page navigation components that allow
|
||||
// the page to only show one pane at a time.
|
||||
this.navigationHelper.GoBackCommand = new ModernKeePass.Common.RelayCommand(() => this.GoBack(), () => this.CanGoBack());
|
||||
this.itemListView.SelectionChanged += itemListView_SelectionChanged;
|
||||
|
||||
// Start listening for Window size changes
|
||||
// to change from showing two panes to showing a single pane
|
||||
Window.Current.SizeChanged += Window_SizeChanged;
|
||||
this.InvalidateVisualState();
|
||||
}
|
||||
|
||||
void itemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (this.UsingLogicalPageNavigation())
|
||||
{
|
||||
this.navigationHelper.GoBackCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the page with content passed during navigation. Any saved state is also
|
||||
/// provided when recreating a page from a prior session.
|
||||
/// </summary>
|
||||
/// <param name="sender">
|
||||
/// The source of the event; typically <see cref="Common.NavigationHelper"/>
|
||||
/// </param>
|
||||
/// <param name="e">Event data that provides both the navigation parameter passed to
|
||||
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested and
|
||||
/// a dictionary of state preserved by this page during an earlier
|
||||
/// session. The state will be null the first time a page is visited.</param>
|
||||
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
|
||||
{
|
||||
// TODO: Assign a bindable group to Me.DefaultViewModel("Group")
|
||||
// TODO: Assign a collection of bindable items to Me.DefaultViewModel("Items")
|
||||
|
||||
if (e.PageState == null)
|
||||
{
|
||||
// When this is a new page, select the first item automatically unless logical page
|
||||
// navigation is being used (see the logical page navigation #region below.)
|
||||
if (!this.UsingLogicalPageNavigation() && this.itemsViewSource.View != null)
|
||||
{
|
||||
this.itemsViewSource.View.MoveCurrentToFirst();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restore the previously saved state associated with this page
|
||||
if (e.PageState.ContainsKey("SelectedItem") && this.itemsViewSource.View != null)
|
||||
{
|
||||
// TODO: Invoke Me.itemsViewSource.View.MoveCurrentTo() with the selected
|
||||
// item as specified by the value of pageState("SelectedItem")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preserves state associated with this page in case the application is suspended or the
|
||||
/// page is discarded from the navigation cache. Values must conform to the serialization
|
||||
/// requirements of <see cref="Common.SuspensionManager.SessionState"/>.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event; typically <see cref="Common.NavigationHelper"/></param>
|
||||
/// <param name="e">Event data that provides an empty dictionary to be populated with
|
||||
/// serializable state.</param>
|
||||
private void navigationHelper_SaveState(object sender, SaveStateEventArgs e)
|
||||
{
|
||||
if (this.itemsViewSource.View != null)
|
||||
{
|
||||
// TODO: Derive a serializable navigation parameter and assign it to
|
||||
// pageState("SelectedItem")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#region Logical page navigation
|
||||
|
||||
// The split page is designed so that when the Window does have enough space to show
|
||||
// both the list and the details, only one pane will be shown at at time.
|
||||
//
|
||||
// This is all implemented with a single physical page that can represent two logical
|
||||
// pages. The code below achieves this goal without making the user aware of the
|
||||
// distinction.
|
||||
|
||||
private const int MinimumWidthForSupportingTwoPanes = 768;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked to determine whether the page should act as one logical page or two.
|
||||
/// </summary>
|
||||
/// <returns>True if the window should show act as one logical page, false
|
||||
/// otherwise.</returns>
|
||||
private bool UsingLogicalPageNavigation()
|
||||
{
|
||||
return Window.Current.Bounds.Width < MinimumWidthForSupportingTwoPanes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked with the Window changes size
|
||||
/// </summary>
|
||||
/// <param name="sender">The current Window</param>
|
||||
/// <param name="e">Event data that describes the new size of the Window</param>
|
||||
private void Window_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
|
||||
{
|
||||
this.InvalidateVisualState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when an item within the list is selected.
|
||||
/// </summary>
|
||||
/// <param name="sender">The GridView displaying the selected item.</param>
|
||||
/// <param name="e">Event data that describes how the selection was changed.</param>
|
||||
private void ItemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
// Invalidate the view state when logical page navigation is in effect, as a change
|
||||
// in selection may cause a corresponding change in the current logical page. When
|
||||
// an item is selected this has the effect of changing from displaying the item list
|
||||
// to showing the selected item's details. When the selection is cleared this has the
|
||||
// opposite effect.
|
||||
if (this.UsingLogicalPageNavigation()) this.InvalidateVisualState();
|
||||
}
|
||||
|
||||
private bool CanGoBack()
|
||||
{
|
||||
if (this.UsingLogicalPageNavigation() && this.itemListView.SelectedItem != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.navigationHelper.CanGoBack();
|
||||
}
|
||||
}
|
||||
private void GoBack()
|
||||
{
|
||||
if (this.UsingLogicalPageNavigation() && this.itemListView.SelectedItem != null)
|
||||
{
|
||||
// When logical page navigation is in effect and there's a selected item that
|
||||
// item's details are currently displayed. Clearing the selection will return to
|
||||
// the item list. From the user's point of view this is a logical backward
|
||||
// navigation.
|
||||
this.itemListView.SelectedItem = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.navigationHelper.GoBack();
|
||||
}
|
||||
}
|
||||
|
||||
private void InvalidateVisualState()
|
||||
{
|
||||
var visualState = DetermineVisualState();
|
||||
VisualStateManager.GoToState(this, visualState, false);
|
||||
this.navigationHelper.GoBackCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked to determine the name of the visual state that corresponds to an application
|
||||
/// view state.
|
||||
/// </summary>
|
||||
/// <returns>The name of the desired visual state. This is the same as the name of the
|
||||
/// view state except when there is a selected item in portrait and snapped views where
|
||||
/// this additional logical page is represented by adding a suffix of _Detail.</returns>
|
||||
private string DetermineVisualState()
|
||||
{
|
||||
if (!UsingLogicalPageNavigation())
|
||||
return "PrimaryView";
|
||||
|
||||
// Update the back button's enabled state when the view state changes
|
||||
var logicalPageBack = this.UsingLogicalPageNavigation() && this.itemListView.SelectedItem != null;
|
||||
|
||||
return logicalPageBack ? "SinglePane_Detail" : "SinglePane";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NavigationHelper registration
|
||||
|
||||
/// The methods provided in this section are simply used to allow
|
||||
/// NavigationHelper to respond to the page's navigation methods.
|
||||
///
|
||||
/// Page specific logic should be placed in event handlers for the
|
||||
/// <see cref="Common.NavigationHelper.LoadState"/>
|
||||
/// and <see cref="Common.NavigationHelper.SaveState"/>.
|
||||
/// The navigation parameter is available in the LoadState method
|
||||
/// in addition to page state preserved during an earlier session.
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
navigationHelper.OnNavigatedTo(e);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
navigationHelper.OnNavigatedFrom(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -24,6 +24,6 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.2.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.2.0.0")]
|
||||
[assembly: AssemblyVersion("1.5.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.5.0.0")]
|
||||
[assembly: ComVisible(false)]
|
@@ -7,7 +7,7 @@
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<ContentControl>
|
||||
<Grid Background="Transparent" Margin="0" Width="77" Height="77">
|
||||
<Grid Background="{StaticResource ListViewItemSelectedBackgroundThemeBrush}" Margin="0" Width="50" Height="50">
|
||||
<Canvas x:Name="hampurger_menu" HorizontalAlignment="Center" Height="17" UseLayoutRounding="False" VerticalAlignment="Center" Width="28">
|
||||
<Canvas x:Name="Layer_1" Height="17" Canvas.Left="0" Width="28" Margin="0" RenderTransformOrigin="0.5,0.5">
|
||||
<Canvas.RenderTransform>
|
||||
@@ -36,13 +36,12 @@
|
||||
</Setter>
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="Margin" Value="20" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
</Style>
|
||||
<Style x:Key="HeaderTextBoxStyle" TargetType="TextBox">
|
||||
<Setter Property="FontSize" Value="56"/>
|
||||
<Setter Property="FontSize" Value="40"/>
|
||||
<Setter Property="FontWeight" Value="Light"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<!--<Setter Property="LineHeight" Value="40"/>-->
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
401
ModernKeePass/Styles/ListViewLeftIndicatorStyle.xaml
Normal file
401
ModernKeePass/Styles/ListViewLeftIndicatorStyle.xaml
Normal file
@@ -0,0 +1,401 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:core="using:Microsoft.Xaml.Interactions.Core">
|
||||
|
||||
<!-- Style for Windows.UI.Xaml.Controls.ListViewItem with left selection indicator -->
|
||||
<Style TargetType="ListViewItem" x:Key="ListViewLeftIndicatorItemExpanded">
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="TabNavigation" Value="Local"/>
|
||||
<Setter Property="IsHoldingEnabled" Value="True"/>
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="Padding" Value="10,0,0,0"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListViewItem">
|
||||
<Border x:Name="OuterContainer">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="PointerOverBorder"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SelectedEarmark"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ListViewItemSelectedPointerOverBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<PointerDownThemeAnimation TargetName="ContentContainer" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverPressed">
|
||||
<Storyboard>
|
||||
<PointerDownThemeAnimation TargetName="ContentContainer" />
|
||||
<DoubleAnimation Storyboard.TargetName="PointerOverBorder"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SelectedEarmark"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ListViewItemSelectedPointerOverBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="contentPresenter"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="{ThemeResource ListViewItemDisabledThemeOpacity}" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="FocusStates">
|
||||
<VisualState x:Name="Focused">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisual" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Unfocused"/>
|
||||
<VisualState x:Name="PointerFocused"/>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="SelectionHintStates">
|
||||
<VisualState x:Name="VerticalSelectionHint">
|
||||
<Storyboard>
|
||||
<SwipeHintThemeAnimation TargetName="ContentBorder" ToVerticalOffset="15" ToHorizontalOffset="0" />
|
||||
<SwipeHintThemeAnimation TargetName="SelectedLeftIndicator" ToVerticalOffset="15" ToHorizontalOffset="0" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="HintGlyph"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0:0:0.500">
|
||||
<DiscreteDoubleKeyFrame Value="0.5" KeyTime="0:0:0" />
|
||||
<DiscreteDoubleKeyFrame Value="0" KeyTime="0:0:0.500" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="HorizontalSelectionHint">
|
||||
<Storyboard>
|
||||
<SwipeHintThemeAnimation TargetName="ContentBorder" ToHorizontalOffset="-23" ToVerticalOffset="0" />
|
||||
<SwipeHintThemeAnimation TargetName="SelectedLeftIndicator" ToHorizontalOffset="-23" ToVerticalOffset="0" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="HintGlyph"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0:0:0.500">
|
||||
<DiscreteDoubleKeyFrame Value="0.5" KeyTime="0:0:0" />
|
||||
<DiscreteDoubleKeyFrame Value="0" KeyTime="0:0:0.500" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="NoSelectionHint" />
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition To="NoSelectionHint" GeneratedDuration="0:0:0.65"/>
|
||||
</VisualStateGroup.Transitions>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="SelectionStates">
|
||||
<VisualState x:Name="Unselecting">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="HintGlyphBorder"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Unselected">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="HintGlyphBorder"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="UnselectedPointerOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="HintGlyphBorder"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="contentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ListViewItemSelectedForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="UnselectedSwiping">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="SelectingGlyph"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="0.5" />
|
||||
<DoubleAnimation Storyboard.TargetName="HintGlyphBorder"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Selecting">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="SelectedLeftIndicator"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<DoubleAnimation Storyboard.TargetName="SelectingGlyph"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<DoubleAnimation Storyboard.TargetName="HintGlyphBorder"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="contentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ListViewItemSelectedForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Selected">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="SelectedLeftIndicator"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="contentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ListViewItemSelectedForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SelectedSwiping">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="SelectedLeftIndicator"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="contentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ListViewItemSelectedForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SelectedUnfocused">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="SelectedLeftIndicator"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="contentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ListViewItemSelectedForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="DragStates">
|
||||
<VisualState x:Name="NotDragging" />
|
||||
<VisualState x:Name="Dragging">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="InnerDragContent"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="{ThemeResource ListViewItemDragThemeOpacity}" />
|
||||
<DragItemThemeAnimation TargetName="InnerDragContent" />
|
||||
<FadeOutThemeAnimation TargetName="SelectedLeftIndicator" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="DraggingTarget">
|
||||
<Storyboard>
|
||||
<DropTargetItemThemeAnimation TargetName="OuterContainer" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="MultipleDraggingPrimary">
|
||||
<Storyboard>
|
||||
<!-- These two Opacity animations are required - the FadeInThemeAnimations
|
||||
on the same elements animate an internal Opacity. -->
|
||||
<DoubleAnimation Storyboard.TargetName="MultiArrangeOverlayBackground"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
<DoubleAnimation Storyboard.TargetName="MultiArrangeOverlayText"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="1" />
|
||||
|
||||
<DoubleAnimation Storyboard.TargetName="ContentBorder"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
Duration="0"
|
||||
To="{ThemeResource ListViewItemDragThemeOpacity}" />
|
||||
<FadeInThemeAnimation TargetName="MultiArrangeOverlayBackground" />
|
||||
<FadeInThemeAnimation TargetName="MultiArrangeOverlayText" />
|
||||
<DragItemThemeAnimation TargetName="ContentBorder" />
|
||||
<FadeOutThemeAnimation TargetName="SelectedLeftIndicator" />
|
||||
<FadeOutThemeAnimation TargetName="PointerOverBorder" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="MultipleDraggingSecondary">
|
||||
<Storyboard>
|
||||
<FadeOutThemeAnimation TargetName="ContentContainer" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition To="NotDragging" GeneratedDuration="0:0:0.2"/>
|
||||
</VisualStateGroup.Transitions>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ReorderHintStates">
|
||||
<VisualState x:Name="NoReorderHint"/>
|
||||
<VisualState x:Name="BottomReorderHint">
|
||||
<Storyboard>
|
||||
<DragOverThemeAnimation TargetName="ReorderHintContent" ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}" Direction="Bottom" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="TopReorderHint">
|
||||
<Storyboard>
|
||||
<DragOverThemeAnimation TargetName="ReorderHintContent" ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}" Direction="Top" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="RightReorderHint">
|
||||
<Storyboard>
|
||||
<DragOverThemeAnimation TargetName="ReorderHintContent" ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}" Direction="Right" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="LeftReorderHint">
|
||||
<Storyboard>
|
||||
<DragOverThemeAnimation TargetName="ReorderHintContent" ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}" Direction="Left" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition To="NoReorderHint" GeneratedDuration="0:0:0.2"/>
|
||||
</VisualStateGroup.Transitions>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="DataVirtualizationStates">
|
||||
<VisualState x:Name="DataAvailable"/>
|
||||
<VisualState x:Name="DataPlaceholder">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextBlock"
|
||||
Storyboard.TargetProperty="Visibility"
|
||||
Duration="0">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderRect"
|
||||
Storyboard.TargetProperty="Visibility"
|
||||
Duration="0">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid x:Name="ReorderHintContent" Background="Transparent">
|
||||
<Path x:Name="SelectingGlyph" Opacity="0" Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="{ThemeResource ListViewItemCheckSelectingThemeBrush}" Height="13" Stretch="Fill" Width="15" HorizontalAlignment="Right" Margin="0,9.5,9.5,0" VerticalAlignment="Top" FlowDirection="LeftToRight"/>
|
||||
<Border x:Name="HintGlyphBorder"
|
||||
Height="40"
|
||||
Width="40"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Opacity="0"
|
||||
Margin="4">
|
||||
<Path x:Name="HintGlyph" Opacity="0" Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="{ThemeResource ListViewItemCheckHintThemeBrush}" Height="13" Stretch="Fill" Width="15" HorizontalAlignment="Right" Margin="0,5.5,5.5,0" VerticalAlignment="Top" FlowDirection="LeftToRight"/>
|
||||
</Border>
|
||||
<Border x:Name="ContentContainer">
|
||||
<Grid x:Name="InnerDragContent">
|
||||
<Rectangle x:Name="PointerOverBorder"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0"
|
||||
Fill="{ThemeResource ListViewItemPointerOverBackgroundThemeBrush}"
|
||||
Margin="0" />
|
||||
<Rectangle x:Name="FocusVisual"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0"
|
||||
StrokeThickness="2"
|
||||
Stroke="{ThemeResource ListViewItemFocusBorderThemeBrush}" />
|
||||
<Rectangle x:Name="SelectionBackground"
|
||||
Margin="0"
|
||||
Fill="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}"
|
||||
Opacity="0" />
|
||||
<Border x:Name="ContentBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Margin="0">
|
||||
<Grid>
|
||||
<ContentPresenter x:Name="contentPresenter"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
Content="{TemplateBinding Content}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Margin="{TemplateBinding Padding}" />
|
||||
<!-- The 'Xg' text simulates the amount of space one line of text will occupy.
|
||||
In the DataPlaceholder state, the Content is not loaded yet so we
|
||||
approximate the size of the item using placeholder text. -->
|
||||
<TextBlock x:Name="PlaceholderTextBlock"
|
||||
Opacity="0"
|
||||
Text="Xg"
|
||||
Foreground="{x:Null}"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
IsHitTestVisible="False"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
<Rectangle x:Name="PlaceholderRect"
|
||||
Visibility="Collapsed"
|
||||
Fill="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"/>
|
||||
<Rectangle x:Name="MultiArrangeOverlayBackground"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0"
|
||||
Fill="{ThemeResource ListViewItemDragBackgroundThemeBrush}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border x:Name="SelectedLeftIndicator"
|
||||
BorderBrush="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}"
|
||||
BorderThickness="5,0,0,0"
|
||||
Opacity="0"/>
|
||||
<Rectangle x:Name="SelectedBorder"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0"
|
||||
Stroke="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}"
|
||||
StrokeThickness="{ThemeResource ListViewItemSelectedBorderThemeThickness}"
|
||||
Margin="0" />
|
||||
<Border x:Name="SelectedCheckMarkOuter"
|
||||
IsHitTestVisible="False"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Margin="4">
|
||||
<Grid x:Name="SelectedCheckMark" Opacity="0" Height="40" Width="40">
|
||||
<Path x:Name="SelectedEarmark" Data="M0,0 L40,0 L40,40 z" Fill="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}" Stretch="Fill"/>
|
||||
<Path Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="{ThemeResource ListViewItemCheckThemeBrush}" Height="13" Stretch="Fill" Width="15" HorizontalAlignment="Right" Margin="0,5.5,5.5,0" VerticalAlignment="Top" FlowDirection="LeftToRight"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<TextBlock x:Name="MultiArrangeOverlayText"
|
||||
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.DragItemsCount}"
|
||||
Foreground="{ThemeResource ListViewItemDragForegroundThemeBrush}"
|
||||
FontFamily="{ThemeResource ContentControlThemeFontFamily}"
|
||||
FontSize="26.667"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0"
|
||||
TextWrapping="Wrap"
|
||||
TextTrimming="WordEllipsis"
|
||||
Margin="18,9,0,0"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
116
ModernKeePass/Styles/NoBorderButtonStyle.xaml
Normal file
116
ModernKeePass/Styles/NoBorderButtonStyle.xaml
Normal file
@@ -0,0 +1,116 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:ModernKeePass.Styles">
|
||||
<!-- Default style for Windows.UI.Xaml.Controls.Button -->
|
||||
<Style TargetType="Button" x:Key="NoBorderButtonStyle">
|
||||
<Setter Property="Background" Value="{ThemeResource ButtonBackgroundThemeBrush}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ButtonForegroundThemeBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderThemeBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="12,4,12,4" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPointerOverBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPointerOverForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPressedBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPressedForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledBackgroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledBorderThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="FocusStates">
|
||||
<VisualState x:Name="Focused">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="FocusVisualWhite"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<DoubleAnimation Storyboard.TargetName="FocusVisualBlack"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Unfocused" />
|
||||
<VisualState x:Name="PointerFocused" />
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Border x:Name="Border"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Margin="0">
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
</Border>
|
||||
<Rectangle x:Name="FocusVisualWhite"
|
||||
IsHitTestVisible="False"
|
||||
Stroke="{ThemeResource FocusVisualWhiteStrokeThemeBrush}"
|
||||
StrokeEndLineCap="Square"
|
||||
StrokeDashArray="1,1"
|
||||
Opacity="0"
|
||||
StrokeDashOffset="1.5" />
|
||||
<Rectangle x:Name="FocusVisualBlack"
|
||||
IsHitTestVisible="False"
|
||||
Stroke="{ThemeResource FocusVisualBlackStrokeThemeBrush}"
|
||||
StrokeEndLineCap="Square"
|
||||
StrokeDashArray="1,1"
|
||||
Opacity="0"
|
||||
StrokeDashOffset="0.5" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
@@ -1,7 +1,7 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace ModernKeePass.Controls
|
||||
namespace ModernKeePass.TemplateSelectors
|
||||
{
|
||||
public class FirstItemDataTemplateSelector: DataTemplateSelector
|
||||
{
|
20
ModernKeePass/ViewModels/AboutVm.cs
Normal file
20
ModernKeePass/ViewModels/AboutVm.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Windows.ApplicationModel;
|
||||
|
||||
namespace ModernKeePass.ViewModels
|
||||
{
|
||||
public class AboutVm
|
||||
{
|
||||
public string Name { get; } = Package.Current.DisplayName;
|
||||
|
||||
public string Version
|
||||
{
|
||||
get
|
||||
{
|
||||
var package = Package.Current;
|
||||
var version = package.Id.Version;
|
||||
|
||||
return $"{version.Major}.{version.Minor}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,21 +1,39 @@
|
||||
using Windows.UI.Text;
|
||||
using System.ComponentModel;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using ModernKeePass.Common;
|
||||
using ModernKeePass.Interfaces;
|
||||
using ModernKeePass.Mappings;
|
||||
using ModernKeePassLib;
|
||||
using ModernKeePassLib.Cryptography.PasswordGenerator;
|
||||
using ModernKeePassLib.Security;
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
|
||||
namespace ModernKeePass.ViewModels
|
||||
{
|
||||
public class EntryVm: NotifyPropertyChangedBase
|
||||
public class EntryVm : INotifyPropertyChanged, IPwEntity
|
||||
{
|
||||
public GroupVm ParentGroup { get; }
|
||||
public PwEntry Entry { get; }
|
||||
|
||||
public System.Drawing.Color? BackgroundColor => Entry?.BackgroundColor;
|
||||
public System.Drawing.Color? ForegroundColor => Entry?.ForegroundColor;
|
||||
public bool IsRevealPasswordEnabled => !string.IsNullOrEmpty(Password);
|
||||
public bool HasExpired => HasExpirationDate && Entry.ExpiryTime < DateTime.Now;
|
||||
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password.ToCharArray());
|
||||
|
||||
public string Title
|
||||
public double PasswordLength { get; set; } = 25;
|
||||
public bool UpperCasePatternSelected { get; set; } = true;
|
||||
public bool LowerCasePatternSelected { get; set; } = true;
|
||||
public bool DigitsPatternSelected { get; set; } = true;
|
||||
public bool MinusPatternSelected { get; set; }
|
||||
public bool UnderscorePatternSelected { get; set; }
|
||||
public bool SpacePatternSelected { get; set; }
|
||||
public bool SpecialPatternSelected { get; set; }
|
||||
public bool BracketsPatternSelected { get; set; }
|
||||
public string CustomChars { get; set; } = string.Empty;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -35,7 +53,12 @@ namespace ModernKeePass.ViewModels
|
||||
public string Password
|
||||
{
|
||||
get { return GetEntryValue(PwDefs.PasswordField); }
|
||||
set { SetEntryValue(PwDefs.PasswordField, value); }
|
||||
set
|
||||
{
|
||||
SetEntryValue(PwDefs.PasswordField, value);
|
||||
NotifyPropertyChanged("Password");
|
||||
NotifyPropertyChanged("PasswordComplexityIndicator");
|
||||
}
|
||||
}
|
||||
public string Url
|
||||
{
|
||||
@@ -53,26 +76,62 @@ namespace ModernKeePass.ViewModels
|
||||
get
|
||||
{
|
||||
if (Entry == null) return Symbol.Add;
|
||||
if (HasExpired) return Symbol.Priority;
|
||||
var result = PwIconToSegoeMapping.GetSymbolFromIcon(Entry.IconId);
|
||||
return result == Symbol.More ? Symbol.Permissions : result;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTimeOffset ExpiryDate
|
||||
{
|
||||
get { return new DateTimeOffset(Entry.ExpiryTime.Date); }
|
||||
set { if (HasExpirationDate) Entry.ExpiryTime = value.DateTime; }
|
||||
}
|
||||
public TimeSpan ExpiryTime
|
||||
{
|
||||
get { return Entry.ExpiryTime.TimeOfDay; }
|
||||
set { if (HasExpirationDate) Entry.ExpiryTime = Entry.ExpiryTime.Date.Add(value); }
|
||||
}
|
||||
|
||||
public bool IsEditMode
|
||||
{
|
||||
get { return _isEditMode; }
|
||||
set { SetProperty(ref _isEditMode, value); }
|
||||
set
|
||||
{
|
||||
_isEditMode = value;
|
||||
NotifyPropertyChanged("IsEditMode");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRevealPassword
|
||||
{
|
||||
get { return _isRevealPassword; }
|
||||
set { SetProperty(ref _isRevealPassword, value); }
|
||||
set
|
||||
{
|
||||
_isRevealPassword = value;
|
||||
NotifyPropertyChanged("IsRevealPassword");
|
||||
}
|
||||
}
|
||||
public bool HasExpirationDate
|
||||
{
|
||||
get { return Entry.Expires; }
|
||||
set
|
||||
{
|
||||
Entry.Expires = value;
|
||||
NotifyPropertyChanged("HasExpirationDate");
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private bool _isEditMode;
|
||||
private bool _isRevealPassword;
|
||||
|
||||
private void NotifyPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public EntryVm() { }
|
||||
public EntryVm(PwEntry entry, GroupVm parent)
|
||||
{
|
||||
@@ -80,9 +139,34 @@ namespace ModernKeePass.ViewModels
|
||||
ParentGroup = parent;
|
||||
}
|
||||
|
||||
public void RemoveEntry()
|
||||
|
||||
public void GeneratePassword()
|
||||
{
|
||||
ParentGroup.RemoveEntry(this);
|
||||
var pwProfile = new PwProfile()
|
||||
{
|
||||
GeneratorType = PasswordGeneratorType.CharSet,
|
||||
Length = (uint)PasswordLength,
|
||||
CharSet = new PwCharSet()
|
||||
};
|
||||
|
||||
if (UpperCasePatternSelected) pwProfile.CharSet.Add(PwCharSet.UpperCase);
|
||||
if (LowerCasePatternSelected) pwProfile.CharSet.Add(PwCharSet.LowerCase);
|
||||
if (DigitsPatternSelected) pwProfile.CharSet.Add(PwCharSet.Digits);
|
||||
if (SpecialPatternSelected) pwProfile.CharSet.Add(PwCharSet.SpecialChars);
|
||||
if (MinusPatternSelected) pwProfile.CharSet.Add('-');
|
||||
if (UnderscorePatternSelected) pwProfile.CharSet.Add('_');
|
||||
if (SpacePatternSelected) pwProfile.CharSet.Add(' ');
|
||||
if (BracketsPatternSelected) pwProfile.CharSet.Add(PwCharSet.Brackets);
|
||||
|
||||
pwProfile.CharSet.Add(CustomChars);
|
||||
|
||||
ProtectedString password;
|
||||
PwGenerator.Generate(out password, pwProfile, null, new CustomPwGeneratorPool());
|
||||
|
||||
Entry?.Strings.Set(PwDefs.PasswordField, password);
|
||||
NotifyPropertyChanged("Password");
|
||||
NotifyPropertyChanged("IsRevealPasswordEnabled");
|
||||
NotifyPropertyChanged("PasswordComplexityIndicator");
|
||||
}
|
||||
|
||||
private string GetEntryValue(string key)
|
||||
@@ -94,5 +178,27 @@ namespace ModernKeePass.ViewModels
|
||||
{
|
||||
Entry?.Strings.Set(key, new ProtectedString(true, newValue));
|
||||
}
|
||||
|
||||
public void MarkForDelete()
|
||||
{
|
||||
var app = (App)Application.Current;
|
||||
app.PendingDeleteEntities.Add(Id, this);
|
||||
ParentGroup.Entries.Remove(this);
|
||||
}
|
||||
public void CommitDelete()
|
||||
{
|
||||
Entry.ParentGroup.Entries.Remove(Entry);
|
||||
}
|
||||
|
||||
public void UndoDelete()
|
||||
{
|
||||
ParentGroup.Entries.Add(this);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var app = (App)Application.Current;
|
||||
app.Database.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,14 +1,17 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using ModernKeePass.Common;
|
||||
using ModernKeePass.Interfaces;
|
||||
using ModernKeePass.Mappings;
|
||||
using ModernKeePassLib;
|
||||
using System;
|
||||
|
||||
namespace ModernKeePass.ViewModels
|
||||
{
|
||||
public class GroupVm : NotifyPropertyChangedBase
|
||||
public class GroupVm : NotifyPropertyChangedBase, IPwEntity
|
||||
{
|
||||
public GroupVm ParentGroup { get; }
|
||||
public ObservableCollection<EntryVm> Entries { get; set; } = new ObservableCollection<EntryVm>();
|
||||
@@ -19,6 +22,7 @@ namespace ModernKeePass.ViewModels
|
||||
public int GroupCount => Groups.Count - 1;
|
||||
public bool IsNotRoot => ParentGroup != null;
|
||||
public FontWeight FontWeight => _pwGroup == null ? FontWeights.Bold : FontWeights.Normal;
|
||||
public string Id => _pwGroup.Uuid.ToHexString();
|
||||
|
||||
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut
|
||||
{
|
||||
@@ -26,7 +30,7 @@ namespace ModernKeePass.ViewModels
|
||||
{
|
||||
return from e in Entries
|
||||
where e.Entry != null
|
||||
group e by e.Title.FirstOrDefault() into grp
|
||||
group e by e.Name.FirstOrDefault() into grp
|
||||
orderby grp.Key
|
||||
select grp;
|
||||
}
|
||||
@@ -47,13 +51,7 @@ namespace ModernKeePass.ViewModels
|
||||
return result == Symbol.More ? Symbol.Folder : result;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLeftPaneOpen
|
||||
{
|
||||
get { return _isLeftPaneOpen; }
|
||||
set { SetProperty(ref _isLeftPaneOpen, value); }
|
||||
}
|
||||
|
||||
|
||||
public bool IsEditMode
|
||||
{
|
||||
get { return _isEditMode; }
|
||||
@@ -70,36 +68,50 @@ namespace ModernKeePass.ViewModels
|
||||
{
|
||||
_pwGroup = pwGroup;
|
||||
ParentGroup = parent;
|
||||
Entries = new ObservableCollection<EntryVm>(pwGroup.Entries.Select(e => new EntryVm(e, this)).OrderBy(e => e.Title));
|
||||
Entries = new ObservableCollection<EntryVm>(pwGroup.Entries.Select(e => new EntryVm(e, this)).OrderBy(e => e.Name));
|
||||
Entries.Insert(0, new EntryVm ());
|
||||
Groups = new ObservableCollection<GroupVm>(pwGroup.Groups.Select(g => new GroupVm(g, this)).OrderBy(g => g.Name));
|
||||
Groups.Insert(0, new GroupVm ());
|
||||
}
|
||||
|
||||
public void CreateNewGroup()
|
||||
public GroupVm CreateNewGroup()
|
||||
{
|
||||
var pwGroup = new PwGroup(true, true, "New group", PwIcon.Folder);
|
||||
var pwGroup = new PwGroup(true, true, string.Empty, PwIcon.Folder);
|
||||
_pwGroup.AddGroup(pwGroup, true);
|
||||
Groups.Add(new GroupVm(pwGroup, this));
|
||||
var newGroup = new GroupVm(pwGroup, this) {IsEditMode = true};
|
||||
Groups.Add(newGroup);
|
||||
return newGroup;
|
||||
}
|
||||
|
||||
public void CreateNewEntry()
|
||||
public EntryVm CreateNewEntry()
|
||||
{
|
||||
var pwEntry = new PwEntry(true, true);
|
||||
_pwGroup.AddEntry(pwEntry, true);
|
||||
Entries.Add(new EntryVm(pwEntry, this));
|
||||
}
|
||||
|
||||
public void RemoveGroup()
|
||||
{
|
||||
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
|
||||
ParentGroup.Groups.Remove(this);
|
||||
var newEntry = new EntryVm(pwEntry, this) {IsEditMode = true};
|
||||
Entries.Add(newEntry);
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
public void RemoveEntry(EntryVm entry)
|
||||
public void MarkForDelete()
|
||||
{
|
||||
_pwGroup.Entries.Remove(entry.Entry);
|
||||
Entries.Remove(entry);
|
||||
var app = (App)Application.Current;
|
||||
app.PendingDeleteEntities.Add(Id, this);
|
||||
ParentGroup.Groups.Remove(this);
|
||||
}
|
||||
|
||||
public void CommitDelete()
|
||||
{
|
||||
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
|
||||
}
|
||||
public void UndoDelete()
|
||||
{
|
||||
ParentGroup.Groups.Add(this);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var app = (App)Application.Current;
|
||||
app.Database.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,23 +5,18 @@ using ModernKeePass.Interfaces;
|
||||
|
||||
namespace ModernKeePass.ViewModels
|
||||
{
|
||||
public class MainMenuItemVm: NotifyPropertyChangedBase, IIsEnabled
|
||||
public class MainMenuItemVm: NotifyPropertyChangedBase, IIsEnabled, ISelectableModel
|
||||
{
|
||||
private string _title;
|
||||
private bool _isSelected;
|
||||
|
||||
public string Title
|
||||
{
|
||||
get { return IsEnabled ? _title : _title + " - Coming soon"; }
|
||||
set { _title = value; }
|
||||
}
|
||||
public string Title { get; set; }
|
||||
|
||||
public Type PageType { get; set; }
|
||||
public object Parameter { get; set; }
|
||||
public Frame Destination { get; set; }
|
||||
public int Group { get; set; } = 0;
|
||||
public Symbol SymbolIcon { get; set; }
|
||||
public bool IsEnabled => PageType != null;
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
public bool IsSelected
|
||||
{
|
||||
|
@@ -18,7 +18,7 @@ namespace ModernKeePass.ViewModels
|
||||
|
||||
public StorageFile DatabaseFile { get; private set; }
|
||||
public string Token { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public string Name { get; private set; } = "Recent file";
|
||||
public string Path => DatabaseFile.Path;
|
||||
|
||||
public bool IsSelected
|
||||
|
@@ -1,25 +1,30 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using ModernKeePass.Common;
|
||||
using ModernKeePass.Interfaces;
|
||||
using ModernKeePass.Pages;
|
||||
using ModernKeePass.Pages.BasePages;
|
||||
|
||||
namespace ModernKeePass.ViewModels
|
||||
{
|
||||
public class MainVm : NotifyPropertyChangedBase
|
||||
public class MainVm : NotifyPropertyChangedBase, IHasSelectableObject
|
||||
{
|
||||
private IOrderedEnumerable<IGrouping<int, MainMenuItemVm>> _mainMenuItems;
|
||||
private MainMenuItemVm _selectedItem;
|
||||
|
||||
public string Name { get; } = Package.Current.DisplayName;
|
||||
|
||||
public IOrderedEnumerable<IGrouping<int, MainMenuItemVm>> MainMenuItems
|
||||
{
|
||||
get { return _mainMenuItems; }
|
||||
set { SetProperty(ref _mainMenuItems, value); }
|
||||
}
|
||||
|
||||
public MainMenuItemVm SelectedItem
|
||||
public ISelectableModel SelectedItem
|
||||
{
|
||||
get { return _selectedItem; }
|
||||
set
|
||||
@@ -30,7 +35,7 @@ namespace ModernKeePass.ViewModels
|
||||
_selectedItem.IsSelected = false;
|
||||
}
|
||||
|
||||
SetProperty(ref _selectedItem, value);
|
||||
SetProperty(ref _selectedItem, (MainMenuItemVm)value);
|
||||
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
@@ -45,6 +50,7 @@ namespace ModernKeePass.ViewModels
|
||||
{
|
||||
var app = (App)Application.Current;
|
||||
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
|
||||
var isDatabaseOpen = app.Database != null && app.Database.Status == DatabaseHelper.DatabaseStatus.Opened;
|
||||
|
||||
var mainMenuItems = new ObservableCollection<MainMenuItemVm>
|
||||
{
|
||||
@@ -60,11 +66,15 @@ namespace ModernKeePass.ViewModels
|
||||
new MainMenuItemVm
|
||||
{
|
||||
Title = "Save" , PageType = typeof(SaveDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Save,
|
||||
IsSelected = app.Database != null && app.Database.Status == DatabaseHelper.DatabaseStatus.Opened
|
||||
IsSelected = isDatabaseOpen, IsEnabled = isDatabaseOpen
|
||||
},
|
||||
new MainMenuItemVm {
|
||||
Title = "Recent" , PageType = typeof(RecentDatabasesPage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Copy,
|
||||
IsSelected = (app.Database == null || app.Database.Status == DatabaseHelper.DatabaseStatus.Closed) && mru.Entries.Count > 0
|
||||
IsSelected = (app.Database == null || app.Database.Status == DatabaseHelper.DatabaseStatus.Closed) && mru.Entries.Count > 0, IsEnabled = mru.Entries.Count > 0
|
||||
},
|
||||
new MainMenuItemVm
|
||||
{
|
||||
Title = "About" , PageType = typeof(AboutPage), Destination = destinationFrame, SymbolIcon = Symbol.Help
|
||||
}
|
||||
};
|
||||
// Auto-select the Recent Items menu item if the conditions are met
|
||||
|
21
ModernKeePass/ViewModels/NewVm.cs
Normal file
21
ModernKeePass/ViewModels/NewVm.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using ModernKeePassLib.Cryptography;
|
||||
|
||||
namespace ModernKeePass.ViewModels
|
||||
{
|
||||
public class NewVm : OpenVm
|
||||
{
|
||||
private string _password = string.Empty;
|
||||
|
||||
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password.ToCharArray());
|
||||
public string Password
|
||||
{
|
||||
get { return _password; }
|
||||
set
|
||||
{
|
||||
_password = value;
|
||||
NotifyPropertyChanged("PasswordComplexityIndicator");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -27,7 +27,7 @@ namespace ModernKeePass.ViewModels
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void NotifyPropertyChanged(string propertyName)
|
||||
protected void NotifyPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ namespace ModernKeePass.ViewModels
|
||||
{
|
||||
private RecentItemVm _selectedItem;
|
||||
private ObservableCollection<RecentItemVm> _recentItems = new ObservableCollection<RecentItemVm>();
|
||||
|
||||
|
||||
public ObservableCollection<RecentItemVm> RecentItems
|
||||
{
|
||||
get { return _recentItems; }
|
||||
@@ -36,6 +36,7 @@ namespace ModernKeePass.ViewModels
|
||||
|
||||
public RecentVm()
|
||||
{
|
||||
// TODO: opening the files actually changes the MRU order
|
||||
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
|
||||
foreach (var entry in mru.Entries)
|
||||
{
|
||||
|
@@ -1,34 +1,16 @@
|
||||
using System.ComponentModel;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Xaml;
|
||||
using ModernKeePass.Common;
|
||||
|
||||
namespace ModernKeePass.ViewModels
|
||||
{
|
||||
public class SaveVm: INotifyPropertyChanged
|
||||
public class SaveVm
|
||||
{
|
||||
public bool IsSaveEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
var app = (App)Application.Current;
|
||||
return app.Database.Status == DatabaseHelper.DatabaseStatus.Opened;
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
private void NotifyPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public void Save(bool close = true)
|
||||
{
|
||||
var app = (App)Application.Current;
|
||||
app.Database.Save();
|
||||
if (!close) return;
|
||||
app.Database.Close();
|
||||
NotifyPropertyChanged("IsSaveEnabled");
|
||||
}
|
||||
|
||||
internal void Save(StorageFile file)
|
||||
|
@@ -2,25 +2,26 @@
|
||||
<packages>
|
||||
<package id="Microsoft.NETCore.Platforms" version="2.0.0" targetFramework="win81" />
|
||||
<package id="Microsoft.NETCore.Portable.Compatibility" version="1.0.2" targetFramework="win81" />
|
||||
<package id="ModernKeePassLib" version="2.28.4000" targetFramework="win81" />
|
||||
<package id="NETStandard.Library" version="2.0.0" targetFramework="win81" />
|
||||
<package id="Microsoft.Toolkit.Uwp.Notifications" version="2.0.0" targetFramework="win81" />
|
||||
<package id="ModernKeePassLib" version="2.37.4000" targetFramework="win81" />
|
||||
<package id="NETStandard.Library" version="2.0.1" targetFramework="win81" />
|
||||
<package id="Portable.BouncyCastle" version="1.8.1.3" targetFramework="win81" />
|
||||
<package id="Splat" version="2.0.0" targetFramework="win81" />
|
||||
<package id="System.Collections" version="4.0.11" targetFramework="win81" />
|
||||
<package id="System.Diagnostics.Debug" version="4.0.11" targetFramework="win81" />
|
||||
<package id="System.Globalization" version="4.0.11" targetFramework="win81" />
|
||||
<package id="System.IO" version="4.1.0" targetFramework="win81" />
|
||||
<package id="System.Linq" version="4.1.0" targetFramework="win81" />
|
||||
<package id="System.Collections" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Globalization" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.IO" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Linq" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Net.Requests" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Reflection" version="4.1.0" targetFramework="win81" />
|
||||
<package id="System.Reflection.Extensions" version="4.0.1" targetFramework="win81" />
|
||||
<package id="System.Runtime" version="4.1.0" targetFramework="win81" />
|
||||
<package id="System.Reflection" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Runtime" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Runtime.WindowsRuntime" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Text.Encoding" version="4.0.11" targetFramework="win81" />
|
||||
<package id="System.Threading" version="4.0.11" targetFramework="win81" />
|
||||
<package id="System.Threading.Tasks" version="4.0.11" targetFramework="win81" />
|
||||
<package id="System.Text.Encoding" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Threading" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="win81" />
|
||||
<package id="System.Xml.XmlSerializer" version="4.3.0" targetFramework="win81" />
|
||||
<package id="Validation" version="2.4.15" targetFramework="win81" />
|
||||
|
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using ModernKeePassLib.Serialization;
|
||||
using ModernKeePassLib.Utility;
|
||||
#if KeePassLib
|
||||
using KeePassLib.Cryptography.Cipher;
|
||||
#else
|
||||
@@ -8,58 +11,74 @@ using ModernKeePassLib.Cryptography.Cipher;
|
||||
#endif
|
||||
|
||||
using NUnit.Framework;
|
||||
using Org.BouncyCastle.Crypto.Engines;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace ModernKeePassLib.Test.Shared.Cryptography.Cipher
|
||||
{
|
||||
[TestFixture ()]
|
||||
public class StandardAesEngineTests
|
||||
{
|
||||
[Test ()]
|
||||
public void TestEncryptStream ()
|
||||
[TestFixture()]
|
||||
public class StandardAesEngineTests
|
||||
{
|
||||
// Test vector (official ECB test vector #356)
|
||||
var pbIV = new byte[16];
|
||||
var pbTestKey = new byte[32];
|
||||
var pbTestData = new byte[16];
|
||||
var pbReferenceCT = new byte[16] {
|
||||
0x75, 0xD1, 0x1B, 0x0E, 0x3A, 0x68, 0xC4, 0x22,
|
||||
0x3D, 0x88, 0xDB, 0xF0, 0x17, 0x97, 0x7D, 0xD7
|
||||
// Test vector (official ECB test vector #356)
|
||||
private byte[] pbReferenceCT = new byte[16] {
|
||||
0x75, 0xD1, 0x1B, 0x0E, 0x3A, 0x68, 0xC4, 0x22,
|
||||
0x3D, 0x88, 0xDB, 0xF0, 0x17, 0x97, 0x7D, 0xD7
|
||||
};
|
||||
[Test]
|
||||
public void TestEncryptStream()
|
||||
{
|
||||
byte[] pbIV = new byte[16];
|
||||
byte[] pbTestKey = new byte[32];
|
||||
byte[] pbTestData = new byte[16];
|
||||
pbTestData[0] = 0x04;
|
||||
|
||||
pbTestData[0] = 0x04;
|
||||
var outStream = new MemoryStream(new byte[16]);
|
||||
var aes = new StandardAesEngine();
|
||||
var inStream = aes.EncryptStream(outStream, pbTestKey, pbIV);
|
||||
new BinaryWriter(inStream).Write(pbTestData);
|
||||
//Assert.That(outStream.Position, Is.EqualTo(16));
|
||||
outStream.Position = 0;
|
||||
var outBytes = new BinaryReaderEx(outStream, Encoding.UTF8, string.Empty).ReadBytes(16);
|
||||
Assert.That(outBytes, Is.EqualTo(pbReferenceCT));
|
||||
}
|
||||
|
||||
var outStream = new MemoryStream (new byte[16]);
|
||||
var aes = new StandardAesEngine ();
|
||||
var inStream = aes.EncryptStream (outStream, pbTestKey, pbIV);
|
||||
new BinaryWriter (inStream).Write (pbTestData);
|
||||
Assert.That (outStream.Position, Is.EqualTo (16));
|
||||
outStream.Position = 0;
|
||||
var outBytes = new BinaryReader (outStream).ReadBytes (16);
|
||||
Assert.That(outBytes, Is.EqualTo (pbReferenceCT));
|
||||
}
|
||||
|
||||
[Test ()]
|
||||
public void TestDecryptStream ()
|
||||
[Test]
|
||||
public void TestDecryptStream()
|
||||
{
|
||||
// Test vector (official ECB test vector #356)
|
||||
var pbIV = new byte[16];
|
||||
var pbTestKey = new byte[32];
|
||||
var pbTestData = new byte[16];
|
||||
var pbReferenceCT = new byte[16] {
|
||||
0x75, 0xD1, 0x1B, 0x0E, 0x3A, 0x68, 0xC4, 0x22,
|
||||
0x3D, 0x88, 0xDB, 0xF0, 0x17, 0x97, 0x7D, 0xD7
|
||||
};
|
||||
byte[] pbIV = new byte[16];
|
||||
byte[] pbTestKey = new byte[32];
|
||||
byte[] pbTestData = new byte[16];
|
||||
pbTestData[0] = 0x04;
|
||||
|
||||
pbTestData[0] = 0x04;
|
||||
// Possible Mono Bug? This only works with size >= 48
|
||||
var inStream = new MemoryStream(new byte[32]);
|
||||
inStream.Write(pbReferenceCT, 0, pbReferenceCT.Length);
|
||||
inStream.Position = 0;
|
||||
var aes = new StandardAesEngine();
|
||||
var outStream = aes.DecryptStream(inStream, pbTestKey, pbIV);
|
||||
var outBytes = new BinaryReaderEx(outStream, Encoding.UTF8, string.Empty).ReadBytes(16);
|
||||
Assert.That(outBytes, Is.EqualTo(pbTestData));
|
||||
}
|
||||
|
||||
// Possible Mono Bug? This only works with size >= 48
|
||||
var inStream = new MemoryStream (new byte[48]);
|
||||
inStream.Write (pbReferenceCT, 0, pbReferenceCT.Length);
|
||||
inStream.Position = 0;
|
||||
var aes = new StandardAesEngine ();
|
||||
var outStream = aes.DecryptStream (inStream, pbTestKey, pbIV);
|
||||
var outBytes = new BinaryReader (outStream).ReadBytes (16);
|
||||
Assert.That(outBytes, Is.EqualTo (pbTestData));
|
||||
[Test]
|
||||
public void TestBouncyCastleAes()
|
||||
{
|
||||
byte[] pbIV = new byte[16];
|
||||
byte[] pbTestKey = new byte[32];
|
||||
byte[] pbTestData = new byte[16];
|
||||
/*int i;
|
||||
for (i = 0; i < 16; ++i) pbIV[i] = 0;
|
||||
for (i = 0; i < 32; ++i) pbTestKey[i] = 0;
|
||||
for (i = 0; i < 16; ++i) pbTestData[i] = 0;*/
|
||||
pbTestData[0] = 0x04;
|
||||
|
||||
var aesEngine = new AesEngine();
|
||||
//var parametersWithIv = new ParametersWithIV(new KeyParameter(pbTestKey), pbIV);
|
||||
aesEngine.Init(true, new KeyParameter(pbTestKey));
|
||||
Assert.That(aesEngine.GetBlockSize(), Is.EqualTo(pbTestData.Length));
|
||||
aesEngine.ProcessBlock(pbTestData, 0, pbTestData, 0);
|
||||
//Assert.That(MemUtil.ArraysEqual(pbTestData, pbReferenceCT), Is.False);
|
||||
Assert.That(pbTestData, Is.EqualTo(pbReferenceCT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,34 +1,33 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
|
||||
#if KeePassLib
|
||||
using KeePassLib.Keys;
|
||||
#else
|
||||
using ModernKeePassLib.Cryptography.KeyDerivation;
|
||||
using ModernKeePassLib.Keys;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Test.Shared.Keys
|
||||
{
|
||||
[TestFixture ()]
|
||||
public class CompositeKeyTests
|
||||
{
|
||||
[Test ()]
|
||||
[Test]
|
||||
public void TestGenerateKey32 ()
|
||||
{
|
||||
var originalKey = new byte[32];
|
||||
var expectedKey = new byte[32] {
|
||||
var originalKey = new byte[32];
|
||||
var expectedKey = new byte[32] {
|
||||
0xF0, 0xED, 0x57, 0xD5, 0xF0, 0xDA, 0xF3, 0x47,
|
||||
0x90, 0xD0, 0xDB, 0x43, 0x25, 0xC6, 0x81, 0x2C,
|
||||
0x81, 0x6A, 0x0D, 0x94, 0x96, 0xA9, 0x03, 0xE1,
|
||||
0x20, 0xD4, 0x3A, 0x3E, 0x45, 0xAD, 0x02, 0x65
|
||||
};
|
||||
const ulong rounds = 1;
|
||||
};
|
||||
const ulong rounds = 1;
|
||||
|
||||
var composite = new CompositeKey ();
|
||||
var key = composite.GenerateKey32 (originalKey, rounds);
|
||||
Assert.That (key, Is.Not.Null);
|
||||
var keyData = key.ReadData ();
|
||||
Assert.That (keyData, Is.EqualTo (expectedKey));
|
||||
var composite = new CompositeKey ();
|
||||
AesKdf kdf = new AesKdf();
|
||||
KdfParameters p = kdf.GetDefaultParameters();
|
||||
p.SetUInt64(AesKdf.ParamRounds, rounds);
|
||||
p.SetByteArray(AesKdf.ParamSeed, originalKey);
|
||||
var key = composite.GenerateKey32(p);
|
||||
Assert.That (key, Is.Not.Null);
|
||||
var keyData = key.ReadData ();
|
||||
Assert.That (keyData, Is.EqualTo (expectedKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ModernKeePassLib.Test</RootNamespace>
|
||||
<AssemblyName>ModernKeePassLib.Test</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<NuGetPackageImportStamp>
|
||||
@@ -33,9 +33,8 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Portable.BouncyCastle.1.8.1.1\lib\portable-net4+sl5+wp8+win8+wpa81\crypto.dll</HintPath>
|
||||
<Reference Include="BouncyCastle.Crypto">
|
||||
<HintPath>..\packages\Portable.BouncyCastle.1.8.1.3\lib\net40\BouncyCastle.Crypto.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -40,15 +41,15 @@ namespace ModernKeePassLib.Test.Shared.Serialization
|
||||
"\t<Meta>\r\n" +
|
||||
"\t\t<Generator>" + testLocalizedAppName + "</Generator>\r\n" +
|
||||
"\t\t<DatabaseName>" + testDatabaseName + "</DatabaseName>\r\n" +
|
||||
"\t\t<DatabaseNameChanged>2015-03-14T03:15:26Z</DatabaseNameChanged>\r\n" +
|
||||
"\t\t<DatabaseNameChanged>2017-10-23T08:03:55Z</DatabaseNameChanged>\r\n" +
|
||||
"\t\t<DatabaseDescription>" + testDatabaseDescription + "</DatabaseDescription>\r\n" +
|
||||
"\t\t<DatabaseDescriptionChanged>2015-03-14T03:15:26Z</DatabaseDescriptionChanged>\r\n" +
|
||||
"\t\t<DatabaseDescriptionChanged>2017-10-23T08:03:55Z</DatabaseDescriptionChanged>\r\n" +
|
||||
"\t\t<DefaultUserName>" + testDefaultUserName + "</DefaultUserName>\r\n" +
|
||||
"\t\t<DefaultUserNameChanged>2015-03-14T03:15:26Z</DefaultUserNameChanged>\r\n" +
|
||||
"\t\t<DefaultUserNameChanged>2017-10-23T08:03:55Z</DefaultUserNameChanged>\r\n" +
|
||||
"\t\t<MaintenanceHistoryDays>365</MaintenanceHistoryDays>\r\n" +
|
||||
//"\t\t<Color>" + testColor + "</Color>\r\n" +
|
||||
"\t\t<Color></Color>\r\n" +
|
||||
"\t\t<MasterKeyChanged>2015-03-14T03:15:26Z</MasterKeyChanged>\r\n" +
|
||||
"\t\t<MasterKeyChanged>2017-10-23T08:03:55Z</MasterKeyChanged>\r\n" +
|
||||
"\t\t<MasterKeyChangeRec>-1</MasterKeyChangeRec>\r\n" +
|
||||
"\t\t<MasterKeyChangeForce>-1</MasterKeyChangeForce>\r\n" +
|
||||
"\t\t<MemoryProtection>\r\n" +
|
||||
@@ -60,9 +61,9 @@ namespace ModernKeePassLib.Test.Shared.Serialization
|
||||
"\t\t</MemoryProtection>\r\n" +
|
||||
"\t\t<RecycleBinEnabled>True</RecycleBinEnabled>\r\n" +
|
||||
"\t\t<RecycleBinUUID>AAAAAAAAAAAAAAAAAAAAAA==</RecycleBinUUID>\r\n" +
|
||||
"\t\t<RecycleBinChanged>2015-03-14T03:15:26Z</RecycleBinChanged>\r\n" +
|
||||
"\t\t<RecycleBinChanged>2017-10-23T08:03:55Z</RecycleBinChanged>\r\n" +
|
||||
"\t\t<EntryTemplatesGroup>AAAAAAAAAAAAAAAAAAAAAA==</EntryTemplatesGroup>\r\n" +
|
||||
"\t\t<EntryTemplatesGroupChanged>2015-03-14T03:15:26Z</EntryTemplatesGroupChanged>\r\n" +
|
||||
"\t\t<EntryTemplatesGroupChanged>2017-10-23T08:03:55Z</EntryTemplatesGroupChanged>\r\n" +
|
||||
"\t\t<HistoryMaxItems>10</HistoryMaxItems>\r\n" +
|
||||
"\t\t<HistoryMaxSize>6291456</HistoryMaxSize>\r\n" +
|
||||
"\t\t<LastSelectedGroup>AAAAAAAAAAAAAAAAAAAAAA==</LastSelectedGroup>\r\n" +
|
||||
@@ -77,13 +78,13 @@ namespace ModernKeePassLib.Test.Shared.Serialization
|
||||
"\t\t\t<Notes>" + testRootGroupNotes + "</Notes>\r\n" +
|
||||
"\t\t\t<IconID>49</IconID>\r\n" +
|
||||
"\t\t\t<Times>\r\n" +
|
||||
"\t\t\t\t<CreationTime>2015-03-14T03:15:26Z</CreationTime>\r\n" +
|
||||
"\t\t\t\t<LastModificationTime>2015-03-14T03:15:26Z</LastModificationTime>\r\n" +
|
||||
"\t\t\t\t<LastAccessTime>2015-03-14T03:15:26Z</LastAccessTime>\r\n" +
|
||||
"\t\t\t\t<ExpiryTime>2015-03-14T03:15:26Z</ExpiryTime>\r\n" +
|
||||
"\t\t\t\t<CreationTime>2017-10-23T08:03:55Z</CreationTime>\r\n" +
|
||||
"\t\t\t\t<LastModificationTime>2017-10-23T08:03:55Z</LastModificationTime>\r\n" +
|
||||
"\t\t\t\t<LastAccessTime>2017-10-23T08:03:55Z</LastAccessTime>\r\n" +
|
||||
"\t\t\t\t<ExpiryTime>2017-10-23T08:03:55Z</ExpiryTime>\r\n" +
|
||||
"\t\t\t\t<Expires>False</Expires>\r\n" +
|
||||
"\t\t\t\t<UsageCount>0</UsageCount>\r\n" +
|
||||
"\t\t\t\t<LocationChanged>2015-03-14T03:15:26Z</LocationChanged>\r\n" +
|
||||
"\t\t\t\t<LocationChanged>2017-10-23T08:03:55Z</LocationChanged>\r\n" +
|
||||
"\t\t\t</Times>\r\n" +
|
||||
"\t\t\t<IsExpanded>True</IsExpanded>\r\n" +
|
||||
"\t\t\t<DefaultAutoTypeSequence>" + testRootGroupDefaultAutoTypeSequence + "</DefaultAutoTypeSequence>\r\n" +
|
||||
@@ -95,7 +96,7 @@ namespace ModernKeePassLib.Test.Shared.Serialization
|
||||
"\t</Root>\r\n" +
|
||||
"</KeePassFile>";
|
||||
|
||||
const string testDate = "2015-03-14T03:15:26Z";
|
||||
const string testDate = "2017-10-23T08:03:55Z";
|
||||
|
||||
[Test()]
|
||||
public void TestLoad()
|
||||
@@ -120,7 +121,8 @@ namespace ModernKeePassLib.Test.Shared.Serialization
|
||||
{
|
||||
var database = new PwDatabase();
|
||||
database.New(new IOConnectionInfo(), new CompositeKey());
|
||||
var date = DateTime.Parse(testDate);
|
||||
var date = DateTime.Parse(testDate, CultureInfo.CurrentCulture, DateTimeStyles.AdjustToUniversal);
|
||||
//var date = DateTime.UtcNow;
|
||||
PwDatabase.LocalizedAppName = testLocalizedAppName;
|
||||
database.Name = testDatabaseName;
|
||||
database.NameChanged = date;
|
||||
|
@@ -24,7 +24,8 @@ namespace ModernKeePassLib.Test.Shared.Utility
|
||||
public void TestLoadImage ()
|
||||
{
|
||||
var testData = Convert.FromBase64String (testImageData);
|
||||
var image = GfxUtil.LoadImage (testData);
|
||||
var image = GfxUtil.ScaleImage(testData, 16, 16).GetAwaiter().GetResult();
|
||||
//var image = GfxUtil.LoadImage(testData);
|
||||
Assert.That (image.Width, Is.EqualTo (16));
|
||||
Assert.That (image.Height, Is.EqualTo (16));
|
||||
}
|
||||
|
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="PInvoke.BCrypt" publicKeyToken="9e300f9f87f04a7a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.5.0.0" newVersion="0.5.0.0" />
|
||||
<assemblyIdentity name="PInvoke.BCrypt" publicKeyToken="9e300f9f87f04a7a" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.5.0.0" newVersion="0.5.0.0"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="PInvoke.NCrypt" publicKeyToken="9e300f9f87f04a7a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.5.0.0" newVersion="0.5.0.0" />
|
||||
<assemblyIdentity name="PInvoke.NCrypt" publicKeyToken="9e300f9f87f04a7a" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.5.0.0" newVersion="0.5.0.0"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Validation" publicKeyToken="2fc06f0d701809a7" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0" />
|
||||
<assemblyIdentity name="Validation" publicKeyToken="2fc06f0d701809a7" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.WindowsRuntime" publicKeyToken="b77a5c561934e089" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.10.0" newVersion="4.0.10.0" />
|
||||
<assemblyIdentity name="System.Runtime.WindowsRuntime" publicKeyToken="b77a5c561934e089" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.10.0" newVersion="4.0.10.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/></startup></configuration>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -25,7 +25,6 @@ using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
#if KeePassLibSD
|
||||
using KeePassLibSD;
|
||||
|
174
ModernKeePassLib/Collections/ProtectedBinarySet.cs
Normal file
174
ModernKeePassLib/Collections/ProtectedBinarySet.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Security;
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
internal sealed class ProtectedBinarySet : IEnumerable<KeyValuePair<int, ProtectedBinary>>
|
||||
{
|
||||
private Dictionary<int, ProtectedBinary> m_d =
|
||||
new Dictionary<int, ProtectedBinary>();
|
||||
|
||||
public ProtectedBinarySet()
|
||||
{
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_d.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<int, ProtectedBinary>> GetEnumerator()
|
||||
{
|
||||
return m_d.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_d.Clear();
|
||||
}
|
||||
|
||||
private int GetFreeID()
|
||||
{
|
||||
int i = m_d.Count;
|
||||
while(m_d.ContainsKey(i)) { ++i; }
|
||||
Debug.Assert(i == m_d.Count); // m_d.Count should be free
|
||||
return i;
|
||||
}
|
||||
|
||||
public ProtectedBinary Get(int iID)
|
||||
{
|
||||
ProtectedBinary pb;
|
||||
if(m_d.TryGetValue(iID, out pb)) return pb;
|
||||
|
||||
// Debug.Assert(false); // No assert
|
||||
return null;
|
||||
}
|
||||
|
||||
public int Find(ProtectedBinary pb)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return -1; }
|
||||
|
||||
// Fast search by reference
|
||||
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
|
||||
{
|
||||
if(object.ReferenceEquals(pb, kvp.Value))
|
||||
{
|
||||
Debug.Assert(pb.Equals(kvp.Value));
|
||||
return kvp.Key;
|
||||
}
|
||||
}
|
||||
|
||||
// Slow search by content
|
||||
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
|
||||
{
|
||||
if(pb.Equals(kvp.Value)) return kvp.Key;
|
||||
}
|
||||
|
||||
// Debug.Assert(false); // No assert
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Set(int iID, ProtectedBinary pb)
|
||||
{
|
||||
if(iID < 0) { Debug.Assert(false); return; }
|
||||
if(pb == null) { Debug.Assert(false); return; }
|
||||
|
||||
m_d[iID] = pb;
|
||||
}
|
||||
|
||||
public void Add(ProtectedBinary pb)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return; }
|
||||
|
||||
int i = Find(pb);
|
||||
if(i >= 0) return; // Exists already
|
||||
|
||||
i = GetFreeID();
|
||||
m_d[i] = pb;
|
||||
}
|
||||
|
||||
public void AddFrom(ProtectedBinaryDictionary d)
|
||||
{
|
||||
if(d == null) { Debug.Assert(false); return; }
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvp in d)
|
||||
{
|
||||
Add(kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFrom(PwGroup pg)
|
||||
{
|
||||
if(pg == null) { Debug.Assert(false); return; }
|
||||
|
||||
EntryHandler eh = delegate(PwEntry pe)
|
||||
{
|
||||
if(pe == null) { Debug.Assert(false); return true; }
|
||||
|
||||
AddFrom(pe.Binaries);
|
||||
foreach(PwEntry peHistory in pe.History)
|
||||
{
|
||||
if(peHistory == null) { Debug.Assert(false); continue; }
|
||||
AddFrom(peHistory.Binaries);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
pg.TraverseTree(TraversalMethod.PreOrder, null, eh);
|
||||
}
|
||||
|
||||
public ProtectedBinary[] ToArray()
|
||||
{
|
||||
int n = m_d.Count;
|
||||
ProtectedBinary[] v = new ProtectedBinary[n];
|
||||
|
||||
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
|
||||
{
|
||||
if((kvp.Key < 0) || (kvp.Key >= n))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
v[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
for(int i = 0; i < n; ++i)
|
||||
{
|
||||
if(v[i] == null)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -283,11 +283,7 @@ namespace ModernKeePassLib.Collections
|
||||
|
||||
public List<string> GetKeys()
|
||||
{
|
||||
List<string> v = new List<string>();
|
||||
|
||||
foreach(string strKey in m_vStrings.Keys) v.Add(strKey);
|
||||
|
||||
return v;
|
||||
return new List<string>(m_vStrings.Keys);
|
||||
}
|
||||
|
||||
public void EnableProtection(string strField, bool bProtect)
|
||||
@@ -299,7 +295,8 @@ namespace ModernKeePassLib.Collections
|
||||
{
|
||||
byte[] pbData = ps.ReadUtf8();
|
||||
Set(strField, new ProtectedString(bProtect, pbData));
|
||||
MemUtil.ZeroByteArray(pbData);
|
||||
|
||||
if(bProtect) MemUtil.ZeroByteArray(pbData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,12 +18,14 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
#if KeePassLibSD
|
||||
using KeePassLibSD;
|
||||
@@ -77,4 +79,154 @@ namespace ModernKeePassLib.Collections
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PwObjectPoolEx
|
||||
{
|
||||
private Dictionary<PwUuid, ulong> m_dUuidToId =
|
||||
new Dictionary<PwUuid, ulong>();
|
||||
private Dictionary<ulong, IStructureItem> m_dIdToItem =
|
||||
new Dictionary<ulong, IStructureItem>();
|
||||
|
||||
private PwObjectPoolEx()
|
||||
{
|
||||
}
|
||||
|
||||
public static PwObjectPoolEx FromGroup(PwGroup pg)
|
||||
{
|
||||
PwObjectPoolEx p = new PwObjectPoolEx();
|
||||
|
||||
if(pg == null) { Debug.Assert(false); return p; }
|
||||
|
||||
ulong uFreeId = 2; // 0 = "not found", 1 is a hole
|
||||
|
||||
p.m_dUuidToId[pg.Uuid] = uFreeId;
|
||||
p.m_dIdToItem[uFreeId] = pg;
|
||||
uFreeId += 2; // Make hole
|
||||
|
||||
p.AddGroupRec(pg, ref uFreeId);
|
||||
return p;
|
||||
}
|
||||
|
||||
private void AddGroupRec(PwGroup pg, ref ulong uFreeId)
|
||||
{
|
||||
if(pg == null) { Debug.Assert(false); return; }
|
||||
|
||||
ulong uId = uFreeId;
|
||||
|
||||
// Consecutive entries must have consecutive IDs
|
||||
foreach(PwEntry pe in pg.Entries)
|
||||
{
|
||||
Debug.Assert(!m_dUuidToId.ContainsKey(pe.Uuid));
|
||||
Debug.Assert(!m_dIdToItem.ContainsValue(pe));
|
||||
|
||||
m_dUuidToId[pe.Uuid] = uId;
|
||||
m_dIdToItem[uId] = pe;
|
||||
++uId;
|
||||
}
|
||||
++uId; // Make hole
|
||||
|
||||
// Consecutive groups must have consecutive IDs
|
||||
foreach(PwGroup pgSub in pg.Groups)
|
||||
{
|
||||
Debug.Assert(!m_dUuidToId.ContainsKey(pgSub.Uuid));
|
||||
Debug.Assert(!m_dIdToItem.ContainsValue(pgSub));
|
||||
|
||||
m_dUuidToId[pgSub.Uuid] = uId;
|
||||
m_dIdToItem[uId] = pgSub;
|
||||
++uId;
|
||||
}
|
||||
++uId; // Make hole
|
||||
|
||||
foreach(PwGroup pgSub in pg.Groups)
|
||||
{
|
||||
AddGroupRec(pgSub, ref uId);
|
||||
}
|
||||
|
||||
uFreeId = uId;
|
||||
}
|
||||
|
||||
public ulong GetIdByUuid(PwUuid pwUuid)
|
||||
{
|
||||
if(pwUuid == null) { Debug.Assert(false); return 0; }
|
||||
|
||||
ulong uId;
|
||||
m_dUuidToId.TryGetValue(pwUuid, out uId);
|
||||
return uId;
|
||||
}
|
||||
|
||||
public IStructureItem GetItemByUuid(PwUuid pwUuid)
|
||||
{
|
||||
if(pwUuid == null) { Debug.Assert(false); return null; }
|
||||
|
||||
ulong uId;
|
||||
if(!m_dUuidToId.TryGetValue(pwUuid, out uId)) return null;
|
||||
Debug.Assert(uId != 0);
|
||||
|
||||
return GetItemById(uId);
|
||||
}
|
||||
|
||||
public IStructureItem GetItemById(ulong uId)
|
||||
{
|
||||
IStructureItem p;
|
||||
m_dIdToItem.TryGetValue(uId, out p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PwObjectBlock<T> : IEnumerable<T>
|
||||
where T : class, ITimeLogger, IStructureItem, IDeepCloneable<T>
|
||||
{
|
||||
private List<T> m_l = new List<T>();
|
||||
|
||||
public T PrimaryItem
|
||||
{
|
||||
get { return ((m_l.Count > 0) ? m_l[0] : null); }
|
||||
}
|
||||
|
||||
private DateTime m_dtLocationChanged = TimeUtil.SafeMinValueUtc;
|
||||
public DateTime LocationChanged
|
||||
{
|
||||
get { return m_dtLocationChanged; }
|
||||
}
|
||||
|
||||
private PwObjectPoolEx m_poolAssoc = null;
|
||||
public PwObjectPoolEx PoolAssoc
|
||||
{
|
||||
get { return m_poolAssoc; }
|
||||
}
|
||||
|
||||
public PwObjectBlock()
|
||||
{
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public override string ToString()
|
||||
{
|
||||
return ("PwObjectBlock, Count = " + m_l.Count.ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_l.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_l.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(T t, DateTime dtLoc, PwObjectPoolEx pool)
|
||||
{
|
||||
if(t == null) { Debug.Assert(false); return; }
|
||||
|
||||
m_l.Add(t);
|
||||
|
||||
if(dtLoc > m_dtLocationChanged)
|
||||
{
|
||||
m_dtLocationChanged = dtLoc;
|
||||
m_poolAssoc = pool;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -32,14 +32,14 @@ using KeePassLibSD;
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
|
||||
IEnumerable<KeyValuePair<string, string>>
|
||||
IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
|
||||
{
|
||||
private SortedDictionary<string, string> m_vDict =
|
||||
private SortedDictionary<string, string> m_dict =
|
||||
new SortedDictionary<string, string>();
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_vDict.Count; }
|
||||
get { return m_dict.Count; }
|
||||
}
|
||||
|
||||
public StringDictionaryEx()
|
||||
@@ -48,39 +48,53 @@ namespace ModernKeePassLib.Collections
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_vDict.GetEnumerator();
|
||||
return m_dict.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
|
||||
{
|
||||
return m_vDict.GetEnumerator();
|
||||
return m_dict.GetEnumerator();
|
||||
}
|
||||
|
||||
public StringDictionaryEx CloneDeep()
|
||||
{
|
||||
StringDictionaryEx plNew = new StringDictionaryEx();
|
||||
StringDictionaryEx sdNew = new StringDictionaryEx();
|
||||
|
||||
foreach(KeyValuePair<string, string> kvpStr in m_vDict)
|
||||
plNew.Set(kvpStr.Key, kvpStr.Value);
|
||||
foreach(KeyValuePair<string, string> kvp in m_dict)
|
||||
sdNew.m_dict[kvp.Key] = kvp.Value; // Strings are immutable
|
||||
|
||||
return plNew;
|
||||
return sdNew;
|
||||
}
|
||||
|
||||
public bool Equals(StringDictionaryEx sdOther)
|
||||
{
|
||||
if(sdOther == null) { Debug.Assert(false); return false; }
|
||||
|
||||
if(m_dict.Count != sdOther.m_dict.Count) return false;
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in sdOther.m_dict)
|
||||
{
|
||||
string str = Get(kvp.Key);
|
||||
if((str == null) || (str != kvp.Value)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string Get(string strName)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
|
||||
|
||||
string s;
|
||||
if(m_vDict.TryGetValue(strName, out s)) return s;
|
||||
|
||||
if(m_dict.TryGetValue(strName, out s)) return s;
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool Exists(string strName)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
|
||||
|
||||
return m_vDict.ContainsKey(strName);
|
||||
return m_dict.ContainsKey(strName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,25 +106,25 @@ namespace ModernKeePassLib.Collections
|
||||
/// parameters is <c>null</c>.</exception>
|
||||
public void Set(string strField, string strNewValue)
|
||||
{
|
||||
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
|
||||
Debug.Assert(strNewValue != null); if(strNewValue == null) throw new ArgumentNullException("strNewValue");
|
||||
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
|
||||
if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("strNewValue"); }
|
||||
|
||||
m_vDict[strField] = strNewValue;
|
||||
m_dict[strField] = strNewValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a string.
|
||||
/// </summary>
|
||||
/// <param name="strField">Name of the string field to delete.</param>
|
||||
/// <returns>Returns <c>true</c> if the field has been successfully
|
||||
/// removed, otherwise the return value is <c>false</c>.</returns>
|
||||
/// <returns>Returns <c>true</c>, if the field has been successfully
|
||||
/// removed. Otherwise, the return value is <c>false</c>.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the input
|
||||
/// parameter is <c>null</c>.</exception>
|
||||
public bool Remove(string strField)
|
||||
{
|
||||
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
|
||||
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
|
||||
|
||||
return m_vDict.Remove(strField);
|
||||
return m_dict.Remove(strField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
415
ModernKeePassLib/Collections/VariantDictionary.cs
Normal file
415
ModernKeePassLib/Collections/VariantDictionary.cs
Normal file
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
public class VariantDictionary
|
||||
{
|
||||
private const ushort VdVersion = 0x0100;
|
||||
private const ushort VdmCritical = 0xFF00;
|
||||
private const ushort VdmInfo = 0x00FF;
|
||||
|
||||
private Dictionary<string, object> m_d = new Dictionary<string, object>();
|
||||
|
||||
private enum VdType : byte
|
||||
{
|
||||
None = 0,
|
||||
|
||||
// Byte = 0x02,
|
||||
// UInt16 = 0x03,
|
||||
UInt32 = 0x04,
|
||||
UInt64 = 0x05,
|
||||
|
||||
// Signed mask: 0x08
|
||||
Bool = 0x08,
|
||||
// SByte = 0x0A,
|
||||
// Int16 = 0x0B,
|
||||
Int32 = 0x0C,
|
||||
Int64 = 0x0D,
|
||||
|
||||
// Float = 0x10,
|
||||
// Double = 0x11,
|
||||
// Decimal = 0x12,
|
||||
|
||||
// Char = 0x17, // 16-bit Unicode character
|
||||
String = 0x18,
|
||||
|
||||
// Array mask: 0x40
|
||||
ByteArray = 0x42
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_d.Count; }
|
||||
}
|
||||
|
||||
public VariantDictionary()
|
||||
{
|
||||
Debug.Assert((VdmCritical & VdmInfo) == ushort.MinValue);
|
||||
Debug.Assert((VdmCritical | VdmInfo) == ushort.MaxValue);
|
||||
}
|
||||
|
||||
private bool Get<T>(string strName, out T t)
|
||||
{
|
||||
t = default(T);
|
||||
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
|
||||
|
||||
object o;
|
||||
if(!m_d.TryGetValue(strName, out o)) return false; // No assert
|
||||
|
||||
if(o == null) { Debug.Assert(false); return false; }
|
||||
if(o.GetType() != typeof(T)) { Debug.Assert(false); return false; }
|
||||
|
||||
t = (T)o;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetStruct<T>(string strName, T t)
|
||||
where T : struct
|
||||
{
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
|
||||
|
||||
#if DEBUG
|
||||
T tEx;
|
||||
Get<T>(strName, out tEx); // Assert same type
|
||||
#endif
|
||||
|
||||
m_d[strName] = t;
|
||||
}
|
||||
|
||||
private void SetRef<T>(string strName, T t)
|
||||
where T : class
|
||||
{
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
|
||||
if(t == null) { Debug.Assert(false); return; }
|
||||
|
||||
#if DEBUG
|
||||
T tEx;
|
||||
Get<T>(strName, out tEx); // Assert same type
|
||||
#endif
|
||||
|
||||
m_d[strName] = t;
|
||||
}
|
||||
|
||||
public bool Remove(string strName)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
|
||||
|
||||
return m_d.Remove(strName);
|
||||
}
|
||||
|
||||
public void CopyTo(VariantDictionary d)
|
||||
{
|
||||
if(d == null) { Debug.Assert(false); return; }
|
||||
|
||||
// Do not clear the target
|
||||
foreach(KeyValuePair<string, object> kvp in m_d)
|
||||
{
|
||||
d.m_d[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public Type GetTypeOf(string strName)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
|
||||
|
||||
object o;
|
||||
m_d.TryGetValue(strName, out o);
|
||||
if(o == null) return null; // No assert
|
||||
|
||||
return o.GetType();
|
||||
}
|
||||
|
||||
public uint GetUInt32(string strName, uint uDefault)
|
||||
{
|
||||
uint u;
|
||||
if(Get<uint>(strName, out u)) return u;
|
||||
return uDefault;
|
||||
}
|
||||
|
||||
public void SetUInt32(string strName, uint uValue)
|
||||
{
|
||||
SetStruct<uint>(strName, uValue);
|
||||
}
|
||||
|
||||
public ulong GetUInt64(string strName, ulong uDefault)
|
||||
{
|
||||
ulong u;
|
||||
if(Get<ulong>(strName, out u)) return u;
|
||||
return uDefault;
|
||||
}
|
||||
|
||||
public void SetUInt64(string strName, ulong uValue)
|
||||
{
|
||||
SetStruct<ulong>(strName, uValue);
|
||||
}
|
||||
|
||||
public bool GetBool(string strName, bool bDefault)
|
||||
{
|
||||
bool b;
|
||||
if(Get<bool>(strName, out b)) return b;
|
||||
return bDefault;
|
||||
}
|
||||
|
||||
public void SetBool(string strName, bool bValue)
|
||||
{
|
||||
SetStruct<bool>(strName, bValue);
|
||||
}
|
||||
|
||||
public int GetInt32(string strName, int iDefault)
|
||||
{
|
||||
int i;
|
||||
if(Get<int>(strName, out i)) return i;
|
||||
return iDefault;
|
||||
}
|
||||
|
||||
public void SetInt32(string strName, int iValue)
|
||||
{
|
||||
SetStruct<int>(strName, iValue);
|
||||
}
|
||||
|
||||
public long GetInt64(string strName, long lDefault)
|
||||
{
|
||||
long l;
|
||||
if(Get<long>(strName, out l)) return l;
|
||||
return lDefault;
|
||||
}
|
||||
|
||||
public void SetInt64(string strName, long lValue)
|
||||
{
|
||||
SetStruct<long>(strName, lValue);
|
||||
}
|
||||
|
||||
public string GetString(string strName)
|
||||
{
|
||||
string str;
|
||||
Get<string>(strName, out str);
|
||||
return str;
|
||||
}
|
||||
|
||||
public void SetString(string strName, string strValue)
|
||||
{
|
||||
SetRef<string>(strName, strValue);
|
||||
}
|
||||
|
||||
public byte[] GetByteArray(string strName)
|
||||
{
|
||||
byte[] pb;
|
||||
Get<byte[]>(strName, out pb);
|
||||
return pb;
|
||||
}
|
||||
|
||||
public void SetByteArray(string strName, byte[] pbValue)
|
||||
{
|
||||
SetRef<byte[]>(strName, pbValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a deep copy.
|
||||
/// </summary>
|
||||
public virtual object Clone()
|
||||
{
|
||||
VariantDictionary vdNew = new VariantDictionary();
|
||||
|
||||
foreach(KeyValuePair<string, object> kvp in m_d)
|
||||
{
|
||||
object o = kvp.Value;
|
||||
if(o == null) { Debug.Assert(false); continue; }
|
||||
|
||||
Type t = o.GetType();
|
||||
if(t == typeof(byte[]))
|
||||
{
|
||||
byte[] p = (byte[])o;
|
||||
byte[] pNew = new byte[p.Length];
|
||||
if(p.Length > 0) Array.Copy(p, pNew, p.Length);
|
||||
|
||||
o = pNew;
|
||||
}
|
||||
|
||||
vdNew.m_d[kvp.Key] = o;
|
||||
}
|
||||
|
||||
return vdNew;
|
||||
}
|
||||
|
||||
public static byte[] Serialize(VariantDictionary p)
|
||||
{
|
||||
if(p == null) { Debug.Assert(false); return null; }
|
||||
|
||||
byte[] pbRet;
|
||||
using(MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
MemUtil.Write(ms, MemUtil.UInt16ToBytes(VdVersion));
|
||||
|
||||
foreach(KeyValuePair<string, object> kvp in p.m_d)
|
||||
{
|
||||
string strName = kvp.Key;
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); continue; }
|
||||
byte[] pbName = StrUtil.Utf8.GetBytes(strName);
|
||||
|
||||
object o = kvp.Value;
|
||||
if(o == null) { Debug.Assert(false); continue; }
|
||||
|
||||
Type t = o.GetType();
|
||||
VdType vt = VdType.None;
|
||||
byte[] pbValue = null;
|
||||
if(t == typeof(uint))
|
||||
{
|
||||
vt = VdType.UInt32;
|
||||
pbValue = MemUtil.UInt32ToBytes((uint)o);
|
||||
}
|
||||
else if(t == typeof(ulong))
|
||||
{
|
||||
vt = VdType.UInt64;
|
||||
pbValue = MemUtil.UInt64ToBytes((ulong)o);
|
||||
}
|
||||
else if(t == typeof(bool))
|
||||
{
|
||||
vt = VdType.Bool;
|
||||
pbValue = new byte[1];
|
||||
pbValue[0] = ((bool)o ? (byte)1 : (byte)0);
|
||||
}
|
||||
else if(t == typeof(int))
|
||||
{
|
||||
vt = VdType.Int32;
|
||||
pbValue = MemUtil.Int32ToBytes((int)o);
|
||||
}
|
||||
else if(t == typeof(long))
|
||||
{
|
||||
vt = VdType.Int64;
|
||||
pbValue = MemUtil.Int64ToBytes((long)o);
|
||||
}
|
||||
else if(t == typeof(string))
|
||||
{
|
||||
vt = VdType.String;
|
||||
pbValue = StrUtil.Utf8.GetBytes((string)o);
|
||||
}
|
||||
else if(t == typeof(byte[]))
|
||||
{
|
||||
vt = VdType.ByteArray;
|
||||
pbValue = (byte[])o;
|
||||
}
|
||||
else { Debug.Assert(false); continue; } // Unknown type
|
||||
|
||||
ms.WriteByte((byte)vt);
|
||||
MemUtil.Write(ms, MemUtil.Int32ToBytes(pbName.Length));
|
||||
MemUtil.Write(ms, pbName);
|
||||
MemUtil.Write(ms, MemUtil.Int32ToBytes(pbValue.Length));
|
||||
MemUtil.Write(ms, pbValue);
|
||||
}
|
||||
|
||||
ms.WriteByte((byte)VdType.None);
|
||||
pbRet = ms.ToArray();
|
||||
}
|
||||
|
||||
return pbRet;
|
||||
}
|
||||
|
||||
public static VariantDictionary Deserialize(byte[] pb)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return null; }
|
||||
|
||||
VariantDictionary d = new VariantDictionary();
|
||||
using(MemoryStream ms = new MemoryStream(pb, false))
|
||||
{
|
||||
ushort uVersion = MemUtil.BytesToUInt16(MemUtil.Read(ms, 2));
|
||||
if((uVersion & VdmCritical) > (VdVersion & VdmCritical))
|
||||
throw new FormatException(KLRes.FileNewVerReq);
|
||||
|
||||
while(true)
|
||||
{
|
||||
int iType = ms.ReadByte();
|
||||
if(iType < 0) throw new EndOfStreamException(KLRes.FileCorrupted);
|
||||
byte btType = (byte)iType;
|
||||
if(btType == (byte)VdType.None) break;
|
||||
|
||||
int cbName = MemUtil.BytesToInt32(MemUtil.Read(ms, 4));
|
||||
byte[] pbName = MemUtil.Read(ms, cbName);
|
||||
if(pbName.Length != cbName)
|
||||
throw new EndOfStreamException(KLRes.FileCorrupted);
|
||||
string strName = StrUtil.Utf8.GetString(pbName, 0, pbName.Length);
|
||||
|
||||
int cbValue = MemUtil.BytesToInt32(MemUtil.Read(ms, 4));
|
||||
byte[] pbValue = MemUtil.Read(ms, cbValue);
|
||||
if(pbValue.Length != cbValue)
|
||||
throw new EndOfStreamException(KLRes.FileCorrupted);
|
||||
|
||||
switch(btType)
|
||||
{
|
||||
case (byte)VdType.UInt32:
|
||||
if(cbValue == 4)
|
||||
d.SetUInt32(strName, MemUtil.BytesToUInt32(pbValue));
|
||||
else { Debug.Assert(false); }
|
||||
break;
|
||||
|
||||
case (byte)VdType.UInt64:
|
||||
if(cbValue == 8)
|
||||
d.SetUInt64(strName, MemUtil.BytesToUInt64(pbValue));
|
||||
else { Debug.Assert(false); }
|
||||
break;
|
||||
|
||||
case (byte)VdType.Bool:
|
||||
if(cbValue == 1)
|
||||
d.SetBool(strName, (pbValue[0] != 0));
|
||||
else { Debug.Assert(false); }
|
||||
break;
|
||||
|
||||
case (byte)VdType.Int32:
|
||||
if(cbValue == 4)
|
||||
d.SetInt32(strName, MemUtil.BytesToInt32(pbValue));
|
||||
else { Debug.Assert(false); }
|
||||
break;
|
||||
|
||||
case (byte)VdType.Int64:
|
||||
if(cbValue == 8)
|
||||
d.SetInt64(strName, MemUtil.BytesToInt64(pbValue));
|
||||
else { Debug.Assert(false); }
|
||||
break;
|
||||
|
||||
case (byte)VdType.String:
|
||||
d.SetString(strName, StrUtil.Utf8.GetString(pbValue, 0, pbValue.Length));
|
||||
break;
|
||||
|
||||
case (byte)VdType.ByteArray:
|
||||
d.SetByteArray(strName, pbValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.Assert(false); // Unknown type
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(ms.ReadByte() < 0);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
254
ModernKeePassLib/Cryptography/Cipher/ChaCha20Cipher.cs
Normal file
254
ModernKeePassLib/Cryptography/Cipher/ChaCha20Cipher.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of the ChaCha20 cipher with a 96-bit nonce,
|
||||
/// as specified in RFC 7539.
|
||||
/// https://tools.ietf.org/html/rfc7539
|
||||
/// </summary>
|
||||
public sealed class ChaCha20Cipher : CtrBlockCipher
|
||||
{
|
||||
private uint[] m_s = new uint[16]; // State
|
||||
private uint[] m_x = new uint[16]; // Working buffer
|
||||
|
||||
private bool m_bLargeCounter; // See constructor documentation
|
||||
|
||||
private static readonly uint[] g_sigma = new uint[4] {
|
||||
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
|
||||
};
|
||||
|
||||
private const string StrNameRfc = "ChaCha20 (RFC 7539)";
|
||||
|
||||
public override int BlockSize
|
||||
{
|
||||
get { return 64; }
|
||||
}
|
||||
|
||||
public ChaCha20Cipher(byte[] pbKey32, byte[] pbIV12) :
|
||||
this(pbKey32, pbIV12, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="pbKey32">Key (32 bytes).</param>
|
||||
/// <param name="pbIV12">Nonce (12 bytes).</param>
|
||||
/// <param name="bLargeCounter">If <c>false</c>, the RFC 7539 version
|
||||
/// of ChaCha20 is used. In this case, only 256 GB of data can be
|
||||
/// encrypted securely (because the block counter is a 32-bit variable);
|
||||
/// an attempt to encrypt more data throws an exception.
|
||||
/// If <paramref name="bLargeCounter" /> is <c>true</c>, the 32-bit
|
||||
/// counter overflows to another 32-bit variable (i.e. the counter
|
||||
/// effectively is a 64-bit variable), like in the original ChaCha20
|
||||
/// specification by D. J. Bernstein (which has a 64-bit counter and a
|
||||
/// 64-bit nonce). To be compatible with this version, the 64-bit nonce
|
||||
/// must be stored in the last 8 bytes of <paramref name="pbIV12" />
|
||||
/// and the first 4 bytes must be 0.
|
||||
/// If the IV was generated randomly, a 12-byte IV and a large counter
|
||||
/// can be used to securely encrypt more than 256 GB of data (but note
|
||||
/// this is incompatible with RFC 7539 and the original specification).</param>
|
||||
public ChaCha20Cipher(byte[] pbKey32, byte[] pbIV12, bool bLargeCounter) :
|
||||
base()
|
||||
{
|
||||
if(pbKey32 == null) throw new ArgumentNullException("pbKey32");
|
||||
if(pbKey32.Length != 32) throw new ArgumentOutOfRangeException("pbKey32");
|
||||
if(pbIV12 == null) throw new ArgumentNullException("pbIV12");
|
||||
if(pbIV12.Length != 12) throw new ArgumentOutOfRangeException("pbIV12");
|
||||
|
||||
m_bLargeCounter = bLargeCounter;
|
||||
|
||||
// Key setup
|
||||
m_s[4] = MemUtil.BytesToUInt32(pbKey32, 0);
|
||||
m_s[5] = MemUtil.BytesToUInt32(pbKey32, 4);
|
||||
m_s[6] = MemUtil.BytesToUInt32(pbKey32, 8);
|
||||
m_s[7] = MemUtil.BytesToUInt32(pbKey32, 12);
|
||||
m_s[8] = MemUtil.BytesToUInt32(pbKey32, 16);
|
||||
m_s[9] = MemUtil.BytesToUInt32(pbKey32, 20);
|
||||
m_s[10] = MemUtil.BytesToUInt32(pbKey32, 24);
|
||||
m_s[11] = MemUtil.BytesToUInt32(pbKey32, 28);
|
||||
m_s[0] = g_sigma[0];
|
||||
m_s[1] = g_sigma[1];
|
||||
m_s[2] = g_sigma[2];
|
||||
m_s[3] = g_sigma[3];
|
||||
|
||||
// IV setup
|
||||
m_s[12] = 0; // Counter
|
||||
m_s[13] = MemUtil.BytesToUInt32(pbIV12, 0);
|
||||
m_s[14] = MemUtil.BytesToUInt32(pbIV12, 4);
|
||||
m_s[15] = MemUtil.BytesToUInt32(pbIV12, 8);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool bDisposing)
|
||||
{
|
||||
if(bDisposing)
|
||||
{
|
||||
MemUtil.ZeroArray<uint>(m_s);
|
||||
MemUtil.ZeroArray<uint>(m_x);
|
||||
}
|
||||
|
||||
base.Dispose(bDisposing);
|
||||
}
|
||||
|
||||
protected override void NextBlock(byte[] pBlock)
|
||||
{
|
||||
if(pBlock == null) throw new ArgumentNullException("pBlock");
|
||||
if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
|
||||
|
||||
// x is a local alias for the working buffer; with this,
|
||||
// the compiler/runtime might remove some checks
|
||||
uint[] x = m_x;
|
||||
if(x == null) throw new InvalidOperationException();
|
||||
if(x.Length < 16) throw new InvalidOperationException();
|
||||
|
||||
uint[] s = m_s;
|
||||
if(s == null) throw new InvalidOperationException();
|
||||
if(s.Length < 16) throw new InvalidOperationException();
|
||||
|
||||
Array.Copy(s, x, 16);
|
||||
|
||||
unchecked
|
||||
{
|
||||
// 10 * 8 quarter rounds = 20 rounds
|
||||
for(int i = 0; i < 10; ++i)
|
||||
{
|
||||
// Column quarter rounds
|
||||
x[ 0] += x[ 4];
|
||||
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 0], 16);
|
||||
x[ 8] += x[12];
|
||||
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 8], 12);
|
||||
x[ 0] += x[ 4];
|
||||
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 0], 8);
|
||||
x[ 8] += x[12];
|
||||
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 8], 7);
|
||||
|
||||
x[ 1] += x[ 5];
|
||||
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 1], 16);
|
||||
x[ 9] += x[13];
|
||||
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[ 9], 12);
|
||||
x[ 1] += x[ 5];
|
||||
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 1], 8);
|
||||
x[ 9] += x[13];
|
||||
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[ 9], 7);
|
||||
|
||||
x[ 2] += x[ 6];
|
||||
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 2], 16);
|
||||
x[10] += x[14];
|
||||
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[10], 12);
|
||||
x[ 2] += x[ 6];
|
||||
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 2], 8);
|
||||
x[10] += x[14];
|
||||
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[10], 7);
|
||||
|
||||
x[ 3] += x[ 7];
|
||||
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 3], 16);
|
||||
x[11] += x[15];
|
||||
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[11], 12);
|
||||
x[ 3] += x[ 7];
|
||||
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 3], 8);
|
||||
x[11] += x[15];
|
||||
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[11], 7);
|
||||
|
||||
// Diagonal quarter rounds
|
||||
x[ 0] += x[ 5];
|
||||
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 0], 16);
|
||||
x[10] += x[15];
|
||||
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[10], 12);
|
||||
x[ 0] += x[ 5];
|
||||
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 0], 8);
|
||||
x[10] += x[15];
|
||||
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[10], 7);
|
||||
|
||||
x[ 1] += x[ 6];
|
||||
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 1], 16);
|
||||
x[11] += x[12];
|
||||
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[11], 12);
|
||||
x[ 1] += x[ 6];
|
||||
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 1], 8);
|
||||
x[11] += x[12];
|
||||
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[11], 7);
|
||||
|
||||
x[ 2] += x[ 7];
|
||||
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 2], 16);
|
||||
x[ 8] += x[13];
|
||||
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[ 8], 12);
|
||||
x[ 2] += x[ 7];
|
||||
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 2], 8);
|
||||
x[ 8] += x[13];
|
||||
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[ 8], 7);
|
||||
|
||||
x[ 3] += x[ 4];
|
||||
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 3], 16);
|
||||
x[ 9] += x[14];
|
||||
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 9], 12);
|
||||
x[ 3] += x[ 4];
|
||||
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 3], 8);
|
||||
x[ 9] += x[14];
|
||||
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 9], 7);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 16; ++i) x[i] += s[i];
|
||||
|
||||
for(int i = 0; i < 16; ++i)
|
||||
{
|
||||
int i4 = i << 2;
|
||||
uint xi = x[i];
|
||||
|
||||
pBlock[i4] = (byte)xi;
|
||||
pBlock[i4 + 1] = (byte)(xi >> 8);
|
||||
pBlock[i4 + 2] = (byte)(xi >> 16);
|
||||
pBlock[i4 + 3] = (byte)(xi >> 24);
|
||||
}
|
||||
|
||||
++s[12];
|
||||
if(s[12] == 0)
|
||||
{
|
||||
if(!m_bLargeCounter)
|
||||
throw new InvalidOperationException(
|
||||
KLRes.EncDataTooLarge.Replace(@"{PARAM}", StrNameRfc));
|
||||
++s[13]; // Increment high half of large counter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long Seek(long lOffset, SeekOrigin so)
|
||||
{
|
||||
if(so != SeekOrigin.Begin) throw new NotSupportedException();
|
||||
|
||||
if((lOffset < 0) || ((lOffset & 63) != 0) ||
|
||||
((lOffset >> 6) > (long)uint.MaxValue))
|
||||
throw new ArgumentOutOfRangeException("lOffset");
|
||||
|
||||
m_s[12] = (uint)(lOffset >> 6);
|
||||
InvalidateBlock();
|
||||
|
||||
return lOffset;
|
||||
}
|
||||
}
|
||||
}
|
177
ModernKeePassLib/Cryptography/Cipher/ChaCha20Engine.cs
Normal file
177
ModernKeePassLib/Cryptography/Cipher/ChaCha20Engine.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Resources;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
public sealed class ChaCha20Engine : ICipherEngine2
|
||||
{
|
||||
private PwUuid m_uuid = new PwUuid(new byte[] {
|
||||
0xD6, 0x03, 0x8A, 0x2B, 0x8B, 0x6F, 0x4C, 0xB5,
|
||||
0xA5, 0x24, 0x33, 0x9A, 0x31, 0xDB, 0xB5, 0x9A
|
||||
});
|
||||
|
||||
public PwUuid CipherUuid
|
||||
{
|
||||
get { return m_uuid; }
|
||||
}
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
return ("ChaCha20 (" + KLRes.KeyBits.Replace(@"{PARAM}",
|
||||
"256") + ", RFC 7539)");
|
||||
}
|
||||
}
|
||||
|
||||
public int KeyLength
|
||||
{
|
||||
get { return 32; }
|
||||
}
|
||||
|
||||
public int IVLength
|
||||
{
|
||||
get { return 12; } // 96 bits
|
||||
}
|
||||
|
||||
public Stream EncryptStream(Stream sPlainText, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
return new ChaCha20Stream(sPlainText, true, pbKey, pbIV);
|
||||
}
|
||||
|
||||
public Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
return new ChaCha20Stream(sEncrypted, false, pbKey, pbIV);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ChaCha20Stream : Stream
|
||||
{
|
||||
private Stream m_sBase;
|
||||
private readonly bool m_bWriting;
|
||||
private ChaCha20Cipher m_c;
|
||||
|
||||
private byte[] m_pbBuffer = null;
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return !m_bWriting; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return m_bWriting; }
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { Debug.Assert(false); throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { Debug.Assert(false); throw new NotSupportedException(); }
|
||||
set { Debug.Assert(false); throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public ChaCha20Stream(Stream sBase, bool bWriting, byte[] pbKey32,
|
||||
byte[] pbIV12)
|
||||
{
|
||||
if(sBase == null) throw new ArgumentNullException("sBase");
|
||||
|
||||
m_sBase = sBase;
|
||||
m_bWriting = bWriting;
|
||||
m_c = new ChaCha20Cipher(pbKey32, pbIV12);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool bDisposing)
|
||||
{
|
||||
if(bDisposing)
|
||||
{
|
||||
if(m_sBase != null)
|
||||
{
|
||||
m_c.Dispose();
|
||||
m_c = null;
|
||||
|
||||
m_sBase.Dispose();
|
||||
m_sBase = null;
|
||||
}
|
||||
|
||||
m_pbBuffer = null;
|
||||
}
|
||||
|
||||
base.Dispose(bDisposing);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
Debug.Assert(m_sBase != null);
|
||||
if(m_bWriting && (m_sBase != null)) m_sBase.Flush();
|
||||
}
|
||||
|
||||
public override long Seek(long lOffset, SeekOrigin soOrigin)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long lValue)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] pbBuffer, int iOffset, int nCount)
|
||||
{
|
||||
if(m_bWriting) throw new InvalidOperationException();
|
||||
|
||||
int cbRead = m_sBase.Read(pbBuffer, iOffset, nCount);
|
||||
m_c.Decrypt(pbBuffer, iOffset, cbRead);
|
||||
return cbRead;
|
||||
}
|
||||
|
||||
public override void Write(byte[] pbBuffer, int iOffset, int nCount)
|
||||
{
|
||||
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
|
||||
if(nCount == 0) return;
|
||||
|
||||
if(!m_bWriting) throw new InvalidOperationException();
|
||||
|
||||
if((m_pbBuffer == null) || (m_pbBuffer.Length < nCount))
|
||||
m_pbBuffer = new byte[nCount];
|
||||
Array.Copy(pbBuffer, iOffset, m_pbBuffer, 0, nCount);
|
||||
|
||||
m_c.Encrypt(m_pbBuffer, 0, nCount);
|
||||
m_sBase.Write(m_pbBuffer, 0, nCount);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,7 +22,6 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Security;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
@@ -41,12 +40,17 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_poolGlobal != null) return m_poolGlobal;
|
||||
CipherPool cp = m_poolGlobal;
|
||||
if(cp == null)
|
||||
{
|
||||
cp = new CipherPool();
|
||||
cp.AddCipher(new StandardAesEngine());
|
||||
cp.AddCipher(new ChaCha20Engine());
|
||||
|
||||
m_poolGlobal = new CipherPool();
|
||||
m_poolGlobal.AddCipher(new StandardAesEngine());
|
||||
m_poolGlobal = cp;
|
||||
}
|
||||
|
||||
return m_poolGlobal;
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
|
109
ModernKeePassLib/Cryptography/Cipher/CtrBlockCipher.cs
Normal file
109
ModernKeePassLib/Cryptography/Cipher/CtrBlockCipher.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
public abstract class CtrBlockCipher : IDisposable
|
||||
{
|
||||
private bool m_bDisposed = false;
|
||||
|
||||
private byte[] m_pBlock;
|
||||
private int m_iBlockPos;
|
||||
|
||||
public abstract int BlockSize
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public CtrBlockCipher()
|
||||
{
|
||||
int cb = this.BlockSize;
|
||||
if(cb <= 0) throw new InvalidOperationException("this.BlockSize");
|
||||
|
||||
m_pBlock = new byte[cb];
|
||||
m_iBlockPos = cb;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool bDisposing)
|
||||
{
|
||||
if(bDisposing)
|
||||
{
|
||||
MemUtil.ZeroByteArray(m_pBlock);
|
||||
m_iBlockPos = m_pBlock.Length;
|
||||
|
||||
m_bDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void InvalidateBlock()
|
||||
{
|
||||
m_iBlockPos = m_pBlock.Length;
|
||||
}
|
||||
|
||||
protected abstract void NextBlock(byte[] pBlock);
|
||||
|
||||
public void Encrypt(byte[] m, int iOffset, int cb)
|
||||
{
|
||||
if(m_bDisposed) throw new ObjectDisposedException(null);
|
||||
if(m == null) throw new ArgumentNullException("m");
|
||||
if(iOffset < 0) throw new ArgumentOutOfRangeException("iOffset");
|
||||
if(cb < 0) throw new ArgumentOutOfRangeException("cb");
|
||||
if(iOffset > (m.Length - cb)) throw new ArgumentOutOfRangeException("cb");
|
||||
|
||||
int cbBlock = m_pBlock.Length;
|
||||
|
||||
while(cb > 0)
|
||||
{
|
||||
Debug.Assert(m_iBlockPos <= cbBlock);
|
||||
if(m_iBlockPos == cbBlock)
|
||||
{
|
||||
NextBlock(m_pBlock);
|
||||
m_iBlockPos = 0;
|
||||
}
|
||||
|
||||
int cbCopy = Math.Min(cbBlock - m_iBlockPos, cb);
|
||||
Debug.Assert(cbCopy > 0);
|
||||
|
||||
MemUtil.XorArray(m_pBlock, m_iBlockPos, m, iOffset, cbCopy);
|
||||
|
||||
m_iBlockPos += cbCopy;
|
||||
iOffset += cbCopy;
|
||||
cb -= cbCopy;
|
||||
}
|
||||
}
|
||||
|
||||
public void Decrypt(byte[] m, int iOffset, int cb)
|
||||
{
|
||||
Encrypt(m, iOffset, cb);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -63,4 +63,25 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
/// <returns>Stream, from which the decrypted data can be read.</returns>
|
||||
Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV);
|
||||
}
|
||||
|
||||
public interface ICipherEngine2 : ICipherEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Length of an encryption key in bytes.
|
||||
/// The base <c>ICipherEngine</c> assumes 32.
|
||||
/// </summary>
|
||||
int KeyLength
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Length of the initialization vector in bytes.
|
||||
/// The base <c>ICipherEngine</c> assumes 16.
|
||||
/// </summary>
|
||||
int IVLength
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -17,182 +17,148 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
// Implementation of the Salsa20 cipher, based on the eSTREAM submission.
|
||||
// Implementation of the Salsa20 cipher, based on the eSTREAM
|
||||
// submission by D. J. Bernstein.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
public sealed class Salsa20Cipher : IDisposable
|
||||
public sealed class Salsa20Cipher : CtrBlockCipher
|
||||
{
|
||||
private uint[] m_state = new uint[16];
|
||||
private uint[] m_s = new uint[16]; // State
|
||||
private uint[] m_x = new uint[16]; // Working buffer
|
||||
|
||||
private byte[] m_output = new byte[64];
|
||||
private int m_outputPos = 64;
|
||||
|
||||
private static readonly uint[] m_sigma = new uint[4] {
|
||||
private static readonly uint[] g_sigma = new uint[4] {
|
||||
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
|
||||
};
|
||||
|
||||
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8)
|
||||
public override int BlockSize
|
||||
{
|
||||
KeySetup(pbKey32);
|
||||
IvSetup(pbIV8);
|
||||
get { return 64; }
|
||||
}
|
||||
|
||||
~Salsa20Cipher()
|
||||
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8) : base()
|
||||
{
|
||||
Dispose(false);
|
||||
if(pbKey32 == null) throw new ArgumentNullException("pbKey32");
|
||||
if(pbKey32.Length != 32) throw new ArgumentOutOfRangeException("pbKey32");
|
||||
if(pbIV8 == null) throw new ArgumentNullException("pbIV8");
|
||||
if(pbIV8.Length != 8) throw new ArgumentOutOfRangeException("pbIV8");
|
||||
|
||||
// Key setup
|
||||
m_s[1] = MemUtil.BytesToUInt32(pbKey32, 0);
|
||||
m_s[2] = MemUtil.BytesToUInt32(pbKey32, 4);
|
||||
m_s[3] = MemUtil.BytesToUInt32(pbKey32, 8);
|
||||
m_s[4] = MemUtil.BytesToUInt32(pbKey32, 12);
|
||||
m_s[11] = MemUtil.BytesToUInt32(pbKey32, 16);
|
||||
m_s[12] = MemUtil.BytesToUInt32(pbKey32, 20);
|
||||
m_s[13] = MemUtil.BytesToUInt32(pbKey32, 24);
|
||||
m_s[14] = MemUtil.BytesToUInt32(pbKey32, 28);
|
||||
m_s[0] = g_sigma[0];
|
||||
m_s[5] = g_sigma[1];
|
||||
m_s[10] = g_sigma[2];
|
||||
m_s[15] = g_sigma[3];
|
||||
|
||||
// IV setup
|
||||
m_s[6] = MemUtil.BytesToUInt32(pbIV8, 0);
|
||||
m_s[7] = MemUtil.BytesToUInt32(pbIV8, 4);
|
||||
m_s[8] = 0; // Counter, low
|
||||
m_s[9] = 0; // Counter, high
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
protected override void Dispose(bool bDisposing)
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
if(bDisposing)
|
||||
{
|
||||
MemUtil.ZeroArray<uint>(m_s);
|
||||
MemUtil.ZeroArray<uint>(m_x);
|
||||
}
|
||||
|
||||
base.Dispose(bDisposing);
|
||||
}
|
||||
|
||||
private void Dispose(bool bDisposing)
|
||||
protected override void NextBlock(byte[] pBlock)
|
||||
{
|
||||
// Clear sensitive data
|
||||
Array.Clear(m_state, 0, m_state.Length);
|
||||
Array.Clear(m_x, 0, m_x.Length);
|
||||
}
|
||||
if(pBlock == null) throw new ArgumentNullException("pBlock");
|
||||
if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
|
||||
|
||||
private void NextOutput()
|
||||
{
|
||||
uint[] x = m_x; // Local alias for working buffer
|
||||
|
||||
// Compiler/runtime might remove array bound checks after this
|
||||
// x is a local alias for the working buffer; with this,
|
||||
// the compiler/runtime might remove some checks
|
||||
uint[] x = m_x;
|
||||
if(x == null) throw new InvalidOperationException();
|
||||
if(x.Length < 16) throw new InvalidOperationException();
|
||||
|
||||
Array.Copy(m_state, x, 16);
|
||||
uint[] s = m_s;
|
||||
if(s == null) throw new InvalidOperationException();
|
||||
if(s.Length < 16) throw new InvalidOperationException();
|
||||
|
||||
Array.Copy(s, x, 16);
|
||||
|
||||
unchecked
|
||||
{
|
||||
for(int i = 0; i < 10; ++i) // (int i = 20; i > 0; i -= 2)
|
||||
// 10 * 8 quarter rounds = 20 rounds
|
||||
for(int i = 0; i < 10; ++i)
|
||||
{
|
||||
x[ 4] ^= Rotl32(x[ 0] + x[12], 7);
|
||||
x[ 8] ^= Rotl32(x[ 4] + x[ 0], 9);
|
||||
x[12] ^= Rotl32(x[ 8] + x[ 4], 13);
|
||||
x[ 0] ^= Rotl32(x[12] + x[ 8], 18);
|
||||
x[ 9] ^= Rotl32(x[ 5] + x[ 1], 7);
|
||||
x[13] ^= Rotl32(x[ 9] + x[ 5], 9);
|
||||
x[ 1] ^= Rotl32(x[13] + x[ 9], 13);
|
||||
x[ 5] ^= Rotl32(x[ 1] + x[13], 18);
|
||||
x[14] ^= Rotl32(x[10] + x[ 6], 7);
|
||||
x[ 2] ^= Rotl32(x[14] + x[10], 9);
|
||||
x[ 6] ^= Rotl32(x[ 2] + x[14], 13);
|
||||
x[10] ^= Rotl32(x[ 6] + x[ 2], 18);
|
||||
x[ 3] ^= Rotl32(x[15] + x[11], 7);
|
||||
x[ 7] ^= Rotl32(x[ 3] + x[15], 9);
|
||||
x[11] ^= Rotl32(x[ 7] + x[ 3], 13);
|
||||
x[15] ^= Rotl32(x[11] + x[ 7], 18);
|
||||
x[ 1] ^= Rotl32(x[ 0] + x[ 3], 7);
|
||||
x[ 2] ^= Rotl32(x[ 1] + x[ 0], 9);
|
||||
x[ 3] ^= Rotl32(x[ 2] + x[ 1], 13);
|
||||
x[ 0] ^= Rotl32(x[ 3] + x[ 2], 18);
|
||||
x[ 6] ^= Rotl32(x[ 5] + x[ 4], 7);
|
||||
x[ 7] ^= Rotl32(x[ 6] + x[ 5], 9);
|
||||
x[ 4] ^= Rotl32(x[ 7] + x[ 6], 13);
|
||||
x[ 5] ^= Rotl32(x[ 4] + x[ 7], 18);
|
||||
x[11] ^= Rotl32(x[10] + x[ 9], 7);
|
||||
x[ 8] ^= Rotl32(x[11] + x[10], 9);
|
||||
x[ 9] ^= Rotl32(x[ 8] + x[11], 13);
|
||||
x[10] ^= Rotl32(x[ 9] + x[ 8], 18);
|
||||
x[12] ^= Rotl32(x[15] + x[14], 7);
|
||||
x[13] ^= Rotl32(x[12] + x[15], 9);
|
||||
x[14] ^= Rotl32(x[13] + x[12], 13);
|
||||
x[15] ^= Rotl32(x[14] + x[13], 18);
|
||||
x[ 4] ^= MemUtil.RotateLeft32(x[ 0] + x[12], 7);
|
||||
x[ 8] ^= MemUtil.RotateLeft32(x[ 4] + x[ 0], 9);
|
||||
x[12] ^= MemUtil.RotateLeft32(x[ 8] + x[ 4], 13);
|
||||
x[ 0] ^= MemUtil.RotateLeft32(x[12] + x[ 8], 18);
|
||||
|
||||
x[ 9] ^= MemUtil.RotateLeft32(x[ 5] + x[ 1], 7);
|
||||
x[13] ^= MemUtil.RotateLeft32(x[ 9] + x[ 5], 9);
|
||||
x[ 1] ^= MemUtil.RotateLeft32(x[13] + x[ 9], 13);
|
||||
x[ 5] ^= MemUtil.RotateLeft32(x[ 1] + x[13], 18);
|
||||
|
||||
x[14] ^= MemUtil.RotateLeft32(x[10] + x[ 6], 7);
|
||||
x[ 2] ^= MemUtil.RotateLeft32(x[14] + x[10], 9);
|
||||
x[ 6] ^= MemUtil.RotateLeft32(x[ 2] + x[14], 13);
|
||||
x[10] ^= MemUtil.RotateLeft32(x[ 6] + x[ 2], 18);
|
||||
|
||||
x[ 3] ^= MemUtil.RotateLeft32(x[15] + x[11], 7);
|
||||
x[ 7] ^= MemUtil.RotateLeft32(x[ 3] + x[15], 9);
|
||||
x[11] ^= MemUtil.RotateLeft32(x[ 7] + x[ 3], 13);
|
||||
x[15] ^= MemUtil.RotateLeft32(x[11] + x[ 7], 18);
|
||||
|
||||
x[ 1] ^= MemUtil.RotateLeft32(x[ 0] + x[ 3], 7);
|
||||
x[ 2] ^= MemUtil.RotateLeft32(x[ 1] + x[ 0], 9);
|
||||
x[ 3] ^= MemUtil.RotateLeft32(x[ 2] + x[ 1], 13);
|
||||
x[ 0] ^= MemUtil.RotateLeft32(x[ 3] + x[ 2], 18);
|
||||
|
||||
x[ 6] ^= MemUtil.RotateLeft32(x[ 5] + x[ 4], 7);
|
||||
x[ 7] ^= MemUtil.RotateLeft32(x[ 6] + x[ 5], 9);
|
||||
x[ 4] ^= MemUtil.RotateLeft32(x[ 7] + x[ 6], 13);
|
||||
x[ 5] ^= MemUtil.RotateLeft32(x[ 4] + x[ 7], 18);
|
||||
|
||||
x[11] ^= MemUtil.RotateLeft32(x[10] + x[ 9], 7);
|
||||
x[ 8] ^= MemUtil.RotateLeft32(x[11] + x[10], 9);
|
||||
x[ 9] ^= MemUtil.RotateLeft32(x[ 8] + x[11], 13);
|
||||
x[10] ^= MemUtil.RotateLeft32(x[ 9] + x[ 8], 18);
|
||||
|
||||
x[12] ^= MemUtil.RotateLeft32(x[15] + x[14], 7);
|
||||
x[13] ^= MemUtil.RotateLeft32(x[12] + x[15], 9);
|
||||
x[14] ^= MemUtil.RotateLeft32(x[13] + x[12], 13);
|
||||
x[15] ^= MemUtil.RotateLeft32(x[14] + x[13], 18);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 16; ++i)
|
||||
x[i] += m_state[i];
|
||||
for(int i = 0; i < 16; ++i) x[i] += s[i];
|
||||
|
||||
for(int i = 0; i < 16; ++i)
|
||||
{
|
||||
m_output[i << 2] = (byte)x[i];
|
||||
m_output[(i << 2) + 1] = (byte)(x[i] >> 8);
|
||||
m_output[(i << 2) + 2] = (byte)(x[i] >> 16);
|
||||
m_output[(i << 2) + 3] = (byte)(x[i] >> 24);
|
||||
int i4 = i << 2;
|
||||
uint xi = x[i];
|
||||
|
||||
pBlock[i4] = (byte)xi;
|
||||
pBlock[i4 + 1] = (byte)(xi >> 8);
|
||||
pBlock[i4 + 2] = (byte)(xi >> 16);
|
||||
pBlock[i4 + 3] = (byte)(xi >> 24);
|
||||
}
|
||||
|
||||
m_outputPos = 0;
|
||||
++m_state[8];
|
||||
if(m_state[8] == 0) ++m_state[9];
|
||||
}
|
||||
}
|
||||
|
||||
private static uint Rotl32(uint x, int b)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return ((x << b) | (x >> (32 - b)));
|
||||
}
|
||||
}
|
||||
|
||||
private static uint U8To32Little(byte[] pb, int iOffset)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return ((uint)pb[iOffset] | ((uint)pb[iOffset + 1] << 8) |
|
||||
((uint)pb[iOffset + 2] << 16) | ((uint)pb[iOffset + 3] << 24));
|
||||
}
|
||||
}
|
||||
|
||||
private void KeySetup(byte[] k)
|
||||
{
|
||||
if(k == null) throw new ArgumentNullException("k");
|
||||
if(k.Length != 32) throw new ArgumentException();
|
||||
|
||||
m_state[1] = U8To32Little(k, 0);
|
||||
m_state[2] = U8To32Little(k, 4);
|
||||
m_state[3] = U8To32Little(k, 8);
|
||||
m_state[4] = U8To32Little(k, 12);
|
||||
m_state[11] = U8To32Little(k, 16);
|
||||
m_state[12] = U8To32Little(k, 20);
|
||||
m_state[13] = U8To32Little(k, 24);
|
||||
m_state[14] = U8To32Little(k, 28);
|
||||
m_state[0] = m_sigma[0];
|
||||
m_state[5] = m_sigma[1];
|
||||
m_state[10] = m_sigma[2];
|
||||
m_state[15] = m_sigma[3];
|
||||
}
|
||||
|
||||
private void IvSetup(byte[] pbIV)
|
||||
{
|
||||
if(pbIV == null) throw new ArgumentNullException("pbIV");
|
||||
if(pbIV.Length != 8) throw new ArgumentException();
|
||||
|
||||
m_state[6] = U8To32Little(pbIV, 0);
|
||||
m_state[7] = U8To32Little(pbIV, 4);
|
||||
m_state[8] = 0;
|
||||
m_state[9] = 0;
|
||||
}
|
||||
|
||||
public void Encrypt(byte[] m, int nByteCount, bool bXor)
|
||||
{
|
||||
if(m == null) throw new ArgumentNullException("m");
|
||||
if(nByteCount > m.Length) throw new ArgumentException();
|
||||
|
||||
int nBytesRem = nByteCount, nOffset = 0;
|
||||
while(nBytesRem > 0)
|
||||
{
|
||||
Debug.Assert((m_outputPos >= 0) && (m_outputPos <= 64));
|
||||
if(m_outputPos == 64) NextOutput();
|
||||
Debug.Assert(m_outputPos < 64);
|
||||
|
||||
int nCopy = Math.Min(64 - m_outputPos, nBytesRem);
|
||||
|
||||
if(bXor) MemUtil.XorArray(m_output, m_outputPos, m, nOffset, nCopy);
|
||||
else Array.Copy(m_output, m_outputPos, m, nOffset, nCopy);
|
||||
|
||||
m_outputPos += nCopy;
|
||||
nBytesRem -= nCopy;
|
||||
nOffset += nCopy;
|
||||
++s[8];
|
||||
if(s[8] == 0) ++s[9];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,7 +18,10 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Diagnostics;
|
||||
#if ModernKeePassLib
|
||||
using ModernKeePassLib.Resources;
|
||||
@@ -39,12 +42,12 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
/// </summary>
|
||||
public sealed class StandardAesEngine : ICipherEngine
|
||||
{
|
||||
#if !ModernKeePassLib && !KeePassRT
|
||||
#if !ModernKeePassLib && !KeePassUAP
|
||||
private const CipherMode m_rCipherMode = CipherMode.CBC;
|
||||
private const PaddingMode m_rCipherPadding = PaddingMode.PKCS7;
|
||||
#endif
|
||||
|
||||
private static PwUuid m_uuidAes = null;
|
||||
private static PwUuid g_uuidAes = null;
|
||||
|
||||
/// <summary>
|
||||
/// UUID of the cipher engine. This ID uniquely identifies the
|
||||
@@ -54,26 +57,38 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_uuidAes == null)
|
||||
PwUuid pu = g_uuidAes;
|
||||
if(pu == null)
|
||||
{
|
||||
m_uuidAes = new PwUuid(new byte[]{
|
||||
pu = new PwUuid(new byte[] {
|
||||
0x31, 0xC1, 0xF2, 0xE6, 0xBF, 0x71, 0x43, 0x50,
|
||||
0xBE, 0x58, 0x05, 0x21, 0x6A, 0xFC, 0x5A, 0xFF });
|
||||
g_uuidAes = pu;
|
||||
}
|
||||
|
||||
return m_uuidAes;
|
||||
return pu;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the UUID of this cipher engine as <c>PwUuid</c> object.
|
||||
/// </summary>
|
||||
public PwUuid CipherUuid => StandardAesEngine.AesUuid;
|
||||
public PwUuid CipherUuid
|
||||
{
|
||||
get { return StandardAesEngine.AesUuid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Get a displayable name describing this cipher engine.
|
||||
/// </summary>
|
||||
public string DisplayName { get { return KLRes.EncAlgorithmAes; } }
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
return ("AES/Rijndael (" + KLRes.KeyBits.Replace(@"{PARAM}",
|
||||
"256") + ", FIPS 197)");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateArguments(Stream stream, bool bEncrypt, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
@@ -90,114 +105,69 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
if(bEncrypt)
|
||||
{
|
||||
Debug.Assert(stream.CanWrite);
|
||||
if(stream.CanWrite == false) throw new ArgumentException("Stream must be writable!");
|
||||
if(!stream.CanWrite) throw new ArgumentException("Stream must be writable!");
|
||||
}
|
||||
else // Decrypt
|
||||
{
|
||||
Debug.Assert(stream.CanRead);
|
||||
if(stream.CanRead == false) throw new ArgumentException("Encrypted stream must be readable!");
|
||||
if(!stream.CanRead) throw new ArgumentException("Encrypted stream must be readable!");
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream CreateStream(Stream s, bool bEncrypt, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
ValidateArguments(s, bEncrypt, pbKey, pbIV);
|
||||
StandardAesEngine.ValidateArguments(s, bEncrypt, pbKey, pbIV);
|
||||
|
||||
byte[] pbLocalIV = new byte[16];
|
||||
Array.Copy(pbIV, pbLocalIV, 16);
|
||||
|
||||
byte[] pbLocalKey = new byte[32];
|
||||
Array.Copy(pbKey, pbLocalKey, 32);
|
||||
#if !ModernKeePassLib
|
||||
//#if ModernKeePassLib
|
||||
/*var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.
|
||||
OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
|
||||
var key = provider.CreateSymmetricKey(pbLocalKey);
|
||||
if (bEncrypt)
|
||||
{
|
||||
var encryptor = WinRTCrypto.CryptographicEngine.CreateEncryptor(
|
||||
key, pbLocalIV);
|
||||
return new CryptoStream(s, encryptor, CryptoStreamMode.Write);
|
||||
} else
|
||||
{
|
||||
var decryptor = WinRTCrypto.CryptographicEngine.CreateDecryptor(
|
||||
key, pbLocalIV);
|
||||
return new CryptoStream(s, decryptor, CryptoStreamMode.Read);
|
||||
}
|
||||
*/
|
||||
|
||||
var provider = SymmetricKeyAlgorithmProvider.
|
||||
OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
|
||||
var key = provider.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(pbLocalKey));
|
||||
#if ModernKeePassLib
|
||||
var cbc = new CbcBlockCipher(new AesEngine());
|
||||
var bc = new PaddedBufferedBlockCipher(cbc,
|
||||
new Pkcs7Padding());
|
||||
var kp = new KeyParameter(pbLocalKey);
|
||||
var prmIV = new ParametersWithIV(kp, pbLocalIV);
|
||||
bc.Init(bEncrypt, prmIV);
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
s.CopyTo(ms);
|
||||
var data = CryptographicBuffer.CreateFromByteArray(ms.ToArray());
|
||||
byte[] resultByteArray;
|
||||
if (bEncrypt)
|
||||
{
|
||||
var encrypted = CryptographicEngine.Encrypt(key, data, CryptographicBuffer.CreateFromByteArray(pbLocalIV));
|
||||
CryptographicBuffer.CopyToByteArray(encrypted, out resultByteArray);
|
||||
return new MemoryStream(resultByteArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
var decrypted = CryptographicEngine.Decrypt(key, data, CryptographicBuffer.CreateFromByteArray(pbLocalIV));
|
||||
CryptographicBuffer.CopyToByteArray(decrypted, out resultByteArray);
|
||||
return new MemoryStream(resultByteArray, true);
|
||||
}
|
||||
}
|
||||
|
||||
//#else
|
||||
|
||||
//#if !KeePassRT
|
||||
//#if !ModernKeePassLib
|
||||
RijndaelManaged r = new RijndaelManaged();
|
||||
if(r.BlockSize != 128) // AES block size
|
||||
var cpRead = (bEncrypt ? null : bc);
|
||||
var cpWrite = (bEncrypt ? bc : null);
|
||||
return new CipherStream(s, cpRead, cpWrite);
|
||||
#elif KeePassUAP
|
||||
return StandardAesEngineExt.CreateStream(s, bEncrypt, pbLocalKey, pbLocalIV);
|
||||
#else
|
||||
SymmetricAlgorithm a = CryptoUtil.CreateAes();
|
||||
if(a.BlockSize != 128) // AES block size
|
||||
{
|
||||
Debug.Assert(false);
|
||||
r.BlockSize = 128;
|
||||
a.BlockSize = 128;
|
||||
}
|
||||
|
||||
r.IV = pbLocalIV;
|
||||
r.KeySize = 256;
|
||||
r.Key = pbLocalKey;
|
||||
r.Mode = m_rCipherMode;
|
||||
r.Padding = m_rCipherPadding;
|
||||
a.IV = pbLocalIV;
|
||||
a.KeySize = 256;
|
||||
a.Key = pbLocalKey;
|
||||
a.Mode = m_rCipherMode;
|
||||
a.Padding = m_rCipherPadding;
|
||||
|
||||
ICryptoTransform iTransform = (bEncrypt ? r.CreateEncryptor() : r.CreateDecryptor());
|
||||
ICryptoTransform iTransform = (bEncrypt ? a.CreateEncryptor() : a.CreateDecryptor());
|
||||
Debug.Assert(iTransform != null);
|
||||
if(iTransform == null) throw new SecurityException("Unable to create Rijndael transform!");
|
||||
if(iTransform == null) throw new SecurityException("Unable to create AES transform!");
|
||||
|
||||
return new CryptoStream(s, iTransform, bEncrypt ? CryptoStreamMode.Write :
|
||||
CryptoStreamMode.Read);
|
||||
#else
|
||||
|
||||
AesEngine aes = new AesEngine();
|
||||
CbcBlockCipher cbc = new CbcBlockCipher(aes);
|
||||
PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(cbc,
|
||||
new Pkcs7Padding());
|
||||
KeyParameter kp = new KeyParameter(pbLocalKey);
|
||||
ParametersWithIV prmIV = new ParametersWithIV(kp, pbLocalIV);
|
||||
bc.Init(bEncrypt, prmIV);
|
||||
|
||||
IBufferedCipher cpRead = (bEncrypt ? null : bc);
|
||||
IBufferedCipher cpWrite = (bEncrypt ? bc : null);
|
||||
return new CipherStream(s, cpRead, cpWrite);
|
||||
#endif
|
||||
|
||||
//#endif
|
||||
}
|
||||
}
|
||||
|
||||
public Stream EncryptStream(Stream sPlainText, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
return CreateStream(sPlainText, true, pbKey, pbIV);
|
||||
return StandardAesEngine.CreateStream(sPlainText, true, pbKey, pbIV);
|
||||
}
|
||||
|
||||
public Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
return CreateStream(sEncrypted, false, pbKey, pbIV);
|
||||
return StandardAesEngine.CreateStream(sEncrypted, false, pbKey, pbIV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,46 +18,58 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
#if ModernKeePassLib
|
||||
using ModernKeePassLib.Cryptography.Hash;
|
||||
using Windows.Security.Cryptography;
|
||||
using ModernKeePassLib.Utility;
|
||||
using Windows.Security.Cryptography.Core;
|
||||
#else
|
||||
using System.Security.Cryptography;
|
||||
using System.Windows.Forms;
|
||||
#endif
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Cryptographically strong random number generator. The returned values
|
||||
/// are unpredictable and cannot be reproduced.
|
||||
/// Cryptographically secure pseudo-random number generator.
|
||||
/// The returned values are unpredictable and cannot be reproduced.
|
||||
/// <c>CryptoRandom</c> is a singleton class.
|
||||
/// </summary>
|
||||
public sealed class CryptoRandom
|
||||
{
|
||||
private byte[] m_pbEntropyPool = new byte[64];
|
||||
private uint m_uCounter;
|
||||
#if ModernKeePassLib
|
||||
//private IRandomNumberGenerator m_rng = NetFxCrypto.RandomNumberGenerator;
|
||||
#else
|
||||
private ulong m_uCounter;
|
||||
#if !ModernKeePassLib
|
||||
private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
|
||||
#endif
|
||||
private ulong m_uGeneratedBytesCount = 0;
|
||||
|
||||
private object m_oSyncRoot = new object();
|
||||
private static readonly object g_oSyncRoot = new object();
|
||||
private readonly object m_oSyncRoot = new object();
|
||||
|
||||
private static CryptoRandom m_pInstance = null;
|
||||
private static CryptoRandom g_pInstance = null;
|
||||
public static CryptoRandom Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_pInstance != null) return m_pInstance;
|
||||
CryptoRandom cr;
|
||||
lock(g_oSyncRoot)
|
||||
{
|
||||
cr = g_pInstance;
|
||||
if(cr == null)
|
||||
{
|
||||
cr = new CryptoRandom();
|
||||
g_pInstance = cr;
|
||||
}
|
||||
}
|
||||
|
||||
m_pInstance = new CryptoRandom();
|
||||
return m_pInstance;
|
||||
return cr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,10 +96,13 @@ namespace ModernKeePassLib.Cryptography
|
||||
|
||||
private CryptoRandom()
|
||||
{
|
||||
Random r = new Random();
|
||||
m_uCounter = (uint)r.Next();
|
||||
// Random rWeak = new Random(); // Based on tick count
|
||||
// byte[] pb = new byte[8];
|
||||
// rWeak.NextBytes(pb);
|
||||
// m_uCounter = MemUtil.BytesToUInt64(pb);
|
||||
m_uCounter = (ulong)DateTime.UtcNow.ToBinary();
|
||||
|
||||
AddEntropy(GetSystemData(r));
|
||||
AddEntropy(GetSystemData());
|
||||
AddEntropy(GetCspData());
|
||||
}
|
||||
|
||||
@@ -103,64 +118,52 @@ namespace ModernKeePassLib.Cryptography
|
||||
if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
|
||||
|
||||
byte[] pbNewData = pbEntropy;
|
||||
if(pbEntropy.Length >= 64)
|
||||
if(pbEntropy.Length > 64)
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
/*var shaNew = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha512);
|
||||
pbNewData = shaNew.HashData(pbEntropy);*/
|
||||
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
|
||||
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbEntropy));
|
||||
CryptographicBuffer.CopyToByteArray(buffer, out pbNewData);
|
||||
#if KeePassLibSD
|
||||
using(SHA256Managed shaNew = new SHA256Managed())
|
||||
#else
|
||||
|
||||
#if !KeePassLibSD
|
||||
SHA512Managed shaNew = new SHA512Managed();
|
||||
#else
|
||||
SHA256Managed shaNew = new SHA256Managed();
|
||||
using(SHA512Managed shaNew = new SHA512Managed())
|
||||
#endif
|
||||
pbNewData = shaNew.ComputeHash(pbEntropy);
|
||||
{
|
||||
pbNewData = shaNew.ComputeHash(pbEntropy);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
lock(m_oSyncRoot)
|
||||
{
|
||||
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length);
|
||||
ms.Write(pbNewData, 0, pbNewData.Length);
|
||||
int cbPool = m_pbEntropyPool.Length;
|
||||
int cbNew = pbNewData.Length;
|
||||
|
||||
byte[] pbFinal = ms.ToArray();
|
||||
#if ModernKeePassLib
|
||||
/*var shaPool = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha512);
|
||||
m_pbEntropyPool = shaPool.HashData(pbFinal);*/
|
||||
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
|
||||
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbFinal));
|
||||
CryptographicBuffer.CopyToByteArray(buffer, out m_pbEntropyPool);
|
||||
byte[] pbCmp = new byte[cbPool + cbNew];
|
||||
Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
|
||||
Array.Copy(pbNewData, 0, pbCmp, cbPool, cbNew);
|
||||
|
||||
MemUtil.ZeroByteArray(m_pbEntropyPool);
|
||||
|
||||
#if KeePassLibSD
|
||||
using(SHA256Managed shaPool = new SHA256Managed())
|
||||
#else
|
||||
|
||||
#if !KeePassLibSD
|
||||
Debug.Assert(pbFinal.Length == (64 + pbNewData.Length));
|
||||
SHA512Managed shaPool = new SHA512Managed();
|
||||
#else
|
||||
SHA256Managed shaPool = new SHA256Managed();
|
||||
using(SHA512Managed shaPool = new SHA512Managed())
|
||||
#endif
|
||||
m_pbEntropyPool = shaPool.ComputeHash(pbFinal);
|
||||
{
|
||||
m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
ms.Dispose();
|
||||
MemUtil.ZeroByteArray(pbCmp);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] GetSystemData(Random rWeak)
|
||||
private static byte[] GetSystemData()
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
byte[] pb;
|
||||
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.TickCount);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.Int32ToBytes(Environment.TickCount);
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
pb = TimeUtil.PackTime(DateTime.Now);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.Int64ToBytes(DateTime.UtcNow.ToBinary());
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
// In try-catch for systems without GUI;
|
||||
@@ -168,95 +171,141 @@ namespace ModernKeePassLib.Cryptography
|
||||
try
|
||||
{
|
||||
Point pt = Cursor.Position;
|
||||
pb = MemUtil.UInt32ToBytes((uint)pt.X);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)pt.Y);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.Int32ToBytes(pt.X);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(pt.Y);
|
||||
MemUtil.Write(ms, pb);
|
||||
}
|
||||
catch(Exception) { }
|
||||
#endif
|
||||
|
||||
pb = MemUtil.UInt32ToBytes((uint)rWeak.Next());
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
|
||||
#if ModernKeePassLib
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.CurrentManagedThreadId);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.CurrentManagedThreadId);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
#else
|
||||
pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID());
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
#endif
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
try
|
||||
{
|
||||
#if KeePassUAP
|
||||
string strOS = EnvironmentExt.OSVersion.VersionString;
|
||||
#else
|
||||
string strOS = Environment.OSVersion.VersionString;
|
||||
#endif
|
||||
AddStrHash(ms, strOS);
|
||||
|
||||
pb = MemUtil.Int32ToBytes(Environment.ProcessorCount);
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
#if !KeePassUAP
|
||||
AddStrHash(ms, Environment.CommandLine);
|
||||
|
||||
pb = MemUtil.Int64ToBytes(Environment.WorkingSet);
|
||||
MemUtil.Write(ms, pb);
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try
|
||||
{
|
||||
foreach(DictionaryEntry de in Environment.GetEnvironmentVariables())
|
||||
{
|
||||
AddStrHash(ms, (de.Key as string));
|
||||
AddStrHash(ms, (de.Value as string));
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
#if KeePassUAP
|
||||
pb = DiagnosticsExt.GetProcessEntropy();
|
||||
MemUtil.Write(ms, pb);
|
||||
#elif !KeePassLibSD
|
||||
Process p = null;
|
||||
try
|
||||
{
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)Environment.WorkingSet);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
|
||||
Version v = Environment.OSVersion.Version;
|
||||
pb = MemUtil.UInt32ToBytes((uint)v.GetHashCode());
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
|
||||
p = Process.GetCurrentProcess();
|
||||
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.Handle.ToInt64());
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)p.HandleCount);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)p.Id);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.NonpagedSystemMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PagedMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PagedSystemMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PeakPagedMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PeakVirtualMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PeakWorkingSet64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PrivateMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.StartTime.ToBinary());
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.VirtualMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.WorkingSet64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(p.HandleCount);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(p.Id);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PagedMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary());
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.WorkingSet64);
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
// Not supported in Mono 1.2.6:
|
||||
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
|
||||
// ms.Write(pb, 0, pb.Length);
|
||||
// MemUtil.Write(ms, pb);
|
||||
}
|
||||
catch(Exception) { }
|
||||
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
|
||||
finally
|
||||
{
|
||||
try { if(p != null) p.Dispose(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
CultureInfo ci = CultureInfo.CurrentCulture;
|
||||
if(ci != null)
|
||||
{
|
||||
pb = MemUtil.Int32ToBytes(ci.GetHashCode());
|
||||
MemUtil.Write(ms, pb);
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
pb = Guid.NewGuid().ToByteArray();
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
byte[] pbAll = ms.ToArray();
|
||||
ms.Dispose();
|
||||
return pbAll;
|
||||
}
|
||||
|
||||
private static void AddStrHash(Stream s, string str)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); return; }
|
||||
if(string.IsNullOrEmpty(str)) return;
|
||||
|
||||
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(str);
|
||||
byte[] pbHash = CryptoUtil.HashSha256(pbUtf8);
|
||||
MemUtil.Write(s, pbHash);
|
||||
}
|
||||
|
||||
private byte[] GetCspData()
|
||||
{
|
||||
byte[] pbCspRandom = new byte[32];
|
||||
//m_rng.GetBytes(pbCspRandom);
|
||||
#if ModernKeePassLib
|
||||
CryptographicBuffer.CopyToByteArray(CryptographicBuffer.GenerateRandom(32), out pbCspRandom);
|
||||
#else
|
||||
m_rng.GetBytes(pbCspRandom);
|
||||
#endif
|
||||
return pbCspRandom;
|
||||
}
|
||||
|
||||
@@ -265,39 +314,32 @@ namespace ModernKeePassLib.Cryptography
|
||||
if(this.GenerateRandom256Pre != null)
|
||||
this.GenerateRandom256Pre(this, EventArgs.Empty);
|
||||
|
||||
byte[] pbFinal;
|
||||
byte[] pbCmp;
|
||||
lock(m_oSyncRoot)
|
||||
{
|
||||
unchecked { m_uCounter += 386047; } // Prime number
|
||||
byte[] pbCounter = MemUtil.UInt32ToBytes(m_uCounter);
|
||||
m_uCounter += 0x74D8B29E4D38E161UL; // Prime number
|
||||
byte[] pbCounter = MemUtil.UInt64ToBytes(m_uCounter);
|
||||
|
||||
byte[] pbCspRandom = GetCspData();
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length);
|
||||
ms.Write(pbCounter, 0, pbCounter.Length);
|
||||
ms.Write(pbCspRandom, 0, pbCspRandom.Length);
|
||||
pbFinal = ms.ToArray();
|
||||
Debug.Assert(pbFinal.Length == (m_pbEntropyPool.Length +
|
||||
pbCounter.Length + pbCspRandom.Length));
|
||||
ms.Dispose();
|
||||
int cbPool = m_pbEntropyPool.Length;
|
||||
int cbCtr = pbCounter.Length;
|
||||
int cbCsp = pbCspRandom.Length;
|
||||
|
||||
pbCmp = new byte[cbPool + cbCtr + cbCsp];
|
||||
Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
|
||||
Array.Copy(pbCounter, 0, pbCmp, cbPool, cbCtr);
|
||||
Array.Copy(pbCspRandom, 0, pbCmp, cbPool + cbCtr, cbCsp);
|
||||
|
||||
MemUtil.ZeroByteArray(pbCspRandom);
|
||||
|
||||
m_uGeneratedBytesCount += 32;
|
||||
}
|
||||
|
||||
#if ModernKeePassLib
|
||||
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
|
||||
return sha256.HashData(pbFinal);*/
|
||||
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
|
||||
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbFinal));
|
||||
byte[] result;
|
||||
CryptographicBuffer.CopyToByteArray(buffer, out result);
|
||||
return result;
|
||||
#else
|
||||
SHA256Managed sha256 = new SHA256Managed();
|
||||
return sha256.ComputeHash(pbFinal);
|
||||
#endif
|
||||
}
|
||||
byte[] pbRet = CryptoUtil.HashSha256(pbCmp);
|
||||
MemUtil.ZeroByteArray(pbCmp);
|
||||
return pbRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a number of cryptographically strong random bytes.
|
||||
@@ -308,30 +350,54 @@ namespace ModernKeePassLib.Cryptography
|
||||
/// random bytes.</returns>
|
||||
public byte[] GetRandomBytes(uint uRequestedBytes)
|
||||
{
|
||||
if(uRequestedBytes == 0) return new byte[0]; // Allow zero-length array
|
||||
if(uRequestedBytes == 0) return MemUtil.EmptyByteArray;
|
||||
if(uRequestedBytes > (uint)int.MaxValue)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new ArgumentOutOfRangeException("uRequestedBytes");
|
||||
}
|
||||
|
||||
byte[] pbRes = new byte[uRequestedBytes];
|
||||
long lPos = 0;
|
||||
int cbRem = (int)uRequestedBytes;
|
||||
byte[] pbRes = new byte[cbRem];
|
||||
int iPos = 0;
|
||||
|
||||
while(uRequestedBytes != 0)
|
||||
while(cbRem != 0)
|
||||
{
|
||||
byte[] pbRandom256 = GenerateRandom256();
|
||||
Debug.Assert(pbRandom256.Length == 32);
|
||||
|
||||
long lCopy = (long)((uRequestedBytes < 32) ? uRequestedBytes : 32);
|
||||
int cbCopy = Math.Min(cbRem, pbRandom256.Length);
|
||||
Array.Copy(pbRandom256, 0, pbRes, iPos, cbCopy);
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
Array.Copy(pbRandom256, 0, pbRes, lPos, lCopy);
|
||||
#else
|
||||
Array.Copy(pbRandom256, 0, pbRes, (int)lPos, (int)lCopy);
|
||||
#endif
|
||||
MemUtil.ZeroByteArray(pbRandom256);
|
||||
|
||||
lPos += lCopy;
|
||||
uRequestedBytes -= (uint)lCopy;
|
||||
iPos += cbCopy;
|
||||
cbRem -= cbCopy;
|
||||
}
|
||||
|
||||
Debug.Assert((int)lPos == pbRes.Length);
|
||||
Debug.Assert(iPos == pbRes.Length);
|
||||
return pbRes;
|
||||
}
|
||||
|
||||
private static int g_iWeakSeed = 0;
|
||||
public static Random NewWeakRandom()
|
||||
{
|
||||
long s64 = DateTime.UtcNow.ToBinary();
|
||||
int s32 = (int)((s64 >> 32) ^ s64);
|
||||
|
||||
lock(g_oSyncRoot)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
g_iWeakSeed += 0x78A8C4B7; // Prime number
|
||||
s32 ^= g_iWeakSeed;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent overflow in the Random constructor of .NET 2.0
|
||||
if(s32 == int.MinValue) s32 = int.MaxValue;
|
||||
|
||||
return new Random(s32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,14 +19,15 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Windows.Security.Cryptography.Core;
|
||||
|
||||
#if ModernKeePassLib
|
||||
using Windows.Security.Cryptography;
|
||||
#else
|
||||
using ModernKeePassLib.Cryptography.Hash;
|
||||
#elif !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Cryptography.Cipher;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
@@ -42,6 +43,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
|
||||
/// <summary>
|
||||
/// A variant of the ARCFour algorithm (RC4 incompatible).
|
||||
/// Insecure; for backward compatibility only.
|
||||
/// </summary>
|
||||
ArcFourVariant = 1,
|
||||
|
||||
@@ -50,7 +52,12 @@ namespace ModernKeePassLib.Cryptography
|
||||
/// </summary>
|
||||
Salsa20 = 2,
|
||||
|
||||
Count = 3
|
||||
/// <summary>
|
||||
/// ChaCha20 stream cipher algorithm.
|
||||
/// </summary>
|
||||
ChaCha20 = 3,
|
||||
|
||||
Count = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -59,47 +66,71 @@ namespace ModernKeePassLib.Cryptography
|
||||
/// properties, but for the same seed always the same stream
|
||||
/// is produced, i.e. this class can be used as stream cipher.
|
||||
/// </summary>
|
||||
public sealed class CryptoRandomStream
|
||||
public sealed class CryptoRandomStream : IDisposable
|
||||
{
|
||||
private CrsAlgorithm m_crsAlgorithm;
|
||||
private readonly CrsAlgorithm m_crsAlgorithm;
|
||||
private bool m_bDisposed = false;
|
||||
|
||||
private byte[] m_pbState = null;
|
||||
private byte m_i = 0;
|
||||
private byte m_j = 0;
|
||||
|
||||
private Salsa20Cipher m_salsa20 = null;
|
||||
private ChaCha20Cipher m_chacha20 = null;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new cryptographically secure random stream object.
|
||||
/// </summary>
|
||||
/// <param name="genAlgorithm">Algorithm to use.</param>
|
||||
/// <param name="a">Algorithm to use.</param>
|
||||
/// <param name="pbKey">Initialization key. Must not be <c>null</c> and
|
||||
/// must contain at least 1 byte.</param>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the
|
||||
/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception>
|
||||
/// <exception cref="System.ArgumentException">Thrown if the
|
||||
/// <paramref name="pbKey" /> parameter contains no bytes or the
|
||||
/// algorithm is unknown.</exception>
|
||||
public CryptoRandomStream(CrsAlgorithm genAlgorithm, byte[] pbKey)
|
||||
public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
|
||||
{
|
||||
m_crsAlgorithm = genAlgorithm;
|
||||
if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
|
||||
|
||||
Debug.Assert(pbKey != null); if(pbKey == null) throw new ArgumentNullException("pbKey");
|
||||
int cbKey = pbKey.Length;
|
||||
if(cbKey <= 0)
|
||||
{
|
||||
Debug.Assert(false); // Need at least one byte
|
||||
throw new ArgumentOutOfRangeException("pbKey");
|
||||
}
|
||||
|
||||
uint uKeyLen = (uint)pbKey.Length;
|
||||
Debug.Assert(uKeyLen != 0); if(uKeyLen == 0) throw new ArgumentException();
|
||||
m_crsAlgorithm = a;
|
||||
|
||||
if(genAlgorithm == CrsAlgorithm.ArcFourVariant)
|
||||
if(a == CrsAlgorithm.ChaCha20)
|
||||
{
|
||||
byte[] pbKey32 = new byte[32];
|
||||
byte[] pbIV12 = new byte[12];
|
||||
|
||||
using(SHA512Managed h = new SHA512Managed())
|
||||
{
|
||||
byte[] pbHash = h.ComputeHash(pbKey);
|
||||
Array.Copy(pbHash, pbKey32, 32);
|
||||
Array.Copy(pbHash, 32, pbIV12, 0, 12);
|
||||
MemUtil.ZeroByteArray(pbHash);
|
||||
}
|
||||
|
||||
m_chacha20 = new ChaCha20Cipher(pbKey32, pbIV12, true);
|
||||
}
|
||||
else if(a == CrsAlgorithm.Salsa20)
|
||||
{
|
||||
byte[] pbKey32 = CryptoUtil.HashSha256(pbKey);
|
||||
byte[] pbIV8 = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
|
||||
0x97, 0x20, 0x5D, 0x2A }; // Unique constant
|
||||
|
||||
m_salsa20 = new Salsa20Cipher(pbKey32, pbIV8);
|
||||
}
|
||||
else if(a == CrsAlgorithm.ArcFourVariant)
|
||||
{
|
||||
// Fill the state linearly
|
||||
m_pbState = new byte[256];
|
||||
for(uint w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
|
||||
for(int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
|
||||
|
||||
unchecked
|
||||
{
|
||||
byte j = 0, t;
|
||||
uint inxKey = 0;
|
||||
for(uint w = 0; w < 256; ++w) // Key setup
|
||||
int inxKey = 0;
|
||||
for(int w = 0; w < 256; ++w) // Key setup
|
||||
{
|
||||
j += (byte)(m_pbState[w] + pbKey[inxKey]);
|
||||
|
||||
@@ -108,34 +139,42 @@ namespace ModernKeePassLib.Cryptography
|
||||
m_pbState[j] = t;
|
||||
|
||||
++inxKey;
|
||||
if(inxKey >= uKeyLen) inxKey = 0;
|
||||
if(inxKey >= cbKey) inxKey = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GetRandomBytes(512); // Increases security, see cryptanalysis
|
||||
}
|
||||
else if(genAlgorithm == CrsAlgorithm.Salsa20)
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
|
||||
var pbKey32 = sha256.HashData(pbKey);*/
|
||||
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
|
||||
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbKey));
|
||||
byte[] pbKey32;
|
||||
CryptographicBuffer.CopyToByteArray(buffer, out pbKey32);
|
||||
#else
|
||||
SHA256Managed sha256 = new SHA256Managed();
|
||||
byte[] pbKey32 = sha256.ComputeHash(pbKey);
|
||||
#endif
|
||||
byte[] pbIV = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
|
||||
0x97, 0x20, 0x5D, 0x2A }; // Unique constant
|
||||
|
||||
m_salsa20 = new Salsa20Cipher(pbKey32, pbIV);
|
||||
}
|
||||
else // Unknown algorithm
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new ArgumentException();
|
||||
throw new ArgumentOutOfRangeException("a");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing)
|
||||
{
|
||||
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
|
||||
m_chacha20.Dispose();
|
||||
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
|
||||
m_salsa20.Dispose();
|
||||
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
|
||||
{
|
||||
MemUtil.ZeroByteArray(m_pbState);
|
||||
m_i = 0;
|
||||
m_j = 0;
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
m_bDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,15 +185,24 @@ namespace ModernKeePassLib.Cryptography
|
||||
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
|
||||
public byte[] GetRandomBytes(uint uRequestedCount)
|
||||
{
|
||||
if(uRequestedCount == 0) return new byte[0];
|
||||
if(m_bDisposed) throw new ObjectDisposedException(null);
|
||||
|
||||
byte[] pbRet = new byte[uRequestedCount];
|
||||
if(uRequestedCount == 0) return MemUtil.EmptyByteArray;
|
||||
if(uRequestedCount > (uint)int.MaxValue)
|
||||
throw new ArgumentOutOfRangeException("uRequestedCount");
|
||||
int cb = (int)uRequestedCount;
|
||||
|
||||
if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
|
||||
byte[] pbRet = new byte[cb];
|
||||
|
||||
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
|
||||
m_chacha20.Encrypt(pbRet, 0, cb);
|
||||
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
|
||||
m_salsa20.Encrypt(pbRet, 0, cb);
|
||||
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
for(uint w = 0; w < uRequestedCount; ++w)
|
||||
for(int w = 0; w < cb; ++w)
|
||||
{
|
||||
++m_i;
|
||||
m_j += m_pbState[m_i];
|
||||
@@ -168,8 +216,6 @@ namespace ModernKeePassLib.Cryptography
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
|
||||
m_salsa20.Encrypt(pbRet, pbRet.Length, false);
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
return pbRet;
|
||||
@@ -178,14 +224,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
public ulong GetRandomUInt64()
|
||||
{
|
||||
byte[] pb = GetRandomBytes(8);
|
||||
|
||||
unchecked
|
||||
{
|
||||
return ((ulong)pb[0]) | ((ulong)pb[1] << 8) |
|
||||
((ulong)pb[2] << 16) | ((ulong)pb[3] << 24) |
|
||||
((ulong)pb[4] << 32) | ((ulong)pb[5] << 40) |
|
||||
((ulong)pb[6] << 48) | ((ulong)pb[7] << 56);
|
||||
}
|
||||
return MemUtil.BytesToUInt64(pb);
|
||||
}
|
||||
|
||||
#if CRSBENCHMARK
|
||||
@@ -211,8 +250,10 @@ namespace ModernKeePassLib.Cryptography
|
||||
int nStart = Environment.TickCount;
|
||||
for(int i = 0; i < nRounds; ++i)
|
||||
{
|
||||
CryptoRandomStream c = new CryptoRandomStream(cra, pbKey);
|
||||
c.GetRandomBytes((uint)nDataSize);
|
||||
using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey))
|
||||
{
|
||||
c.GetRandomBytes((uint)nDataSize);
|
||||
}
|
||||
}
|
||||
int nEnd = Environment.TickCount;
|
||||
|
||||
|
169
ModernKeePassLib/Cryptography/CryptoUtil.cs
Normal file
169
ModernKeePassLib/Cryptography/CryptoUtil.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
#if ModernKeePassLib
|
||||
using ModernKeePassLib.Cryptography.Hash;
|
||||
#elif !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public static class CryptoUtil
|
||||
{
|
||||
public static byte[] HashSha256(byte[] pbData)
|
||||
{
|
||||
if(pbData == null) throw new ArgumentNullException("pbData");
|
||||
|
||||
return HashSha256(pbData, 0, pbData.Length);
|
||||
}
|
||||
|
||||
public static byte[] HashSha256(byte[] pbData, int iOffset, int cbCount)
|
||||
{
|
||||
if(pbData == null) throw new ArgumentNullException("pbData");
|
||||
|
||||
#if DEBUG
|
||||
byte[] pbCopy = new byte[pbData.Length];
|
||||
Array.Copy(pbData, pbCopy, pbData.Length);
|
||||
#endif
|
||||
|
||||
byte[] pbHash;
|
||||
using(SHA256Managed h = new SHA256Managed())
|
||||
{
|
||||
pbHash = h.ComputeHash(pbData, iOffset, cbCount);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// Ensure the data has not been modified
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbData, pbCopy));
|
||||
|
||||
Debug.Assert((pbHash != null) && (pbHash.Length == 32));
|
||||
byte[] pbZero = new byte[32];
|
||||
Debug.Assert(!MemUtil.ArraysEqual(pbHash, pbZero));
|
||||
#endif
|
||||
|
||||
return pbHash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a cryptographic key of length <paramref name="cbOut" />
|
||||
/// (in bytes) from <paramref name="pbIn" />.
|
||||
/// </summary>
|
||||
public static byte[] ResizeKey(byte[] pbIn, int iInOffset,
|
||||
int cbIn, int cbOut)
|
||||
{
|
||||
if(pbIn == null) throw new ArgumentNullException("pbIn");
|
||||
if(cbOut < 0) throw new ArgumentOutOfRangeException("cbOut");
|
||||
|
||||
if(cbOut == 0) return MemUtil.EmptyByteArray;
|
||||
|
||||
byte[] pbHash;
|
||||
if(cbOut <= 32) pbHash = HashSha256(pbIn, iInOffset, cbIn);
|
||||
else
|
||||
{
|
||||
using(SHA512Managed h = new SHA512Managed())
|
||||
{
|
||||
pbHash = h.ComputeHash(pbIn, iInOffset, cbIn);
|
||||
}
|
||||
}
|
||||
|
||||
if(cbOut == pbHash.Length) return pbHash;
|
||||
|
||||
byte[] pbRet = new byte[cbOut];
|
||||
if(cbOut < pbHash.Length)
|
||||
Array.Copy(pbHash, pbRet, cbOut);
|
||||
else
|
||||
{
|
||||
int iPos = 0;
|
||||
ulong r = 0;
|
||||
while(iPos < cbOut)
|
||||
{
|
||||
Debug.Assert(pbHash.Length == 64);
|
||||
using(HMACSHA256 h = new HMACSHA256(pbHash))
|
||||
{
|
||||
byte[] pbR = MemUtil.UInt64ToBytes(r);
|
||||
byte[] pbPart = h.ComputeHash(pbR);
|
||||
|
||||
int cbCopy = Math.Min(cbOut - iPos, pbPart.Length);
|
||||
Debug.Assert(cbCopy > 0);
|
||||
|
||||
Array.Copy(pbPart, 0, pbRet, iPos, cbCopy);
|
||||
iPos += cbCopy;
|
||||
++r;
|
||||
|
||||
MemUtil.ZeroByteArray(pbPart);
|
||||
}
|
||||
}
|
||||
Debug.Assert(iPos == cbOut);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
byte[] pbZero = new byte[pbHash.Length];
|
||||
Debug.Assert(!MemUtil.ArraysEqual(pbHash, pbZero));
|
||||
#endif
|
||||
MemUtil.ZeroByteArray(pbHash);
|
||||
return pbRet;
|
||||
}
|
||||
|
||||
#if !ModernKeePassLib
|
||||
private static bool? g_obAesCsp = null;
|
||||
internal static SymmetricAlgorithm CreateAes()
|
||||
{
|
||||
if(g_obAesCsp.HasValue)
|
||||
return (g_obAesCsp.Value ? CreateAesCsp() : new RijndaelManaged());
|
||||
|
||||
SymmetricAlgorithm a = CreateAesCsp();
|
||||
g_obAesCsp = (a != null);
|
||||
return (a ?? new RijndaelManaged());
|
||||
}
|
||||
|
||||
private static SymmetricAlgorithm CreateAesCsp()
|
||||
{
|
||||
try
|
||||
{
|
||||
// On Windows, the CSP implementation is only minimally
|
||||
// faster (and for key derivations it's not used anyway,
|
||||
// as KeePass uses a native implementation based on
|
||||
// CNG/BCrypt, which is much faster)
|
||||
if(!NativeLib.IsUnix()) return null;
|
||||
|
||||
string strFqn = Assembly.CreateQualifiedName(
|
||||
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
|
||||
"System.Security.Cryptography.AesCryptoServiceProvider");
|
||||
|
||||
Type t = Type.GetType(strFqn);
|
||||
if(t == null) return null;
|
||||
|
||||
return (Activator.CreateInstance(t) as SymmetricAlgorithm);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
394
ModernKeePassLib/Cryptography/Hash/Blake2b.cs
Normal file
394
ModernKeePassLib/Cryptography/Hash/Blake2b.cs
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
// This implementation is based on the official reference C
|
||||
// implementation by Samuel Neves (CC0 1.0 Universal).
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
#if ModernKeePassLib
|
||||
#elif !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Hash
|
||||
{
|
||||
public sealed class Blake2b : IDisposable
|
||||
{
|
||||
protected int HashSizeValue;
|
||||
protected internal byte[] HashValue;
|
||||
protected int State = 0;
|
||||
|
||||
private bool m_bDisposed = false;
|
||||
|
||||
private const int NbRounds = 12;
|
||||
private const int NbBlockBytes = 128;
|
||||
private const int NbMaxOutBytes = 64;
|
||||
|
||||
private static readonly ulong[] g_vIV = new ulong[8] {
|
||||
0x6A09E667F3BCC908UL, 0xBB67AE8584CAA73BUL,
|
||||
0x3C6EF372FE94F82BUL, 0xA54FF53A5F1D36F1UL,
|
||||
0x510E527FADE682D1UL, 0x9B05688C2B3E6C1FUL,
|
||||
0x1F83D9ABFB41BD6BUL, 0x5BE0CD19137E2179UL
|
||||
};
|
||||
|
||||
private static readonly int[] g_vSigma = new int[NbRounds * 16] {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
|
||||
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
|
||||
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
|
||||
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
|
||||
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
|
||||
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
|
||||
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
|
||||
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
|
||||
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3
|
||||
};
|
||||
|
||||
private readonly int m_cbHashLength;
|
||||
|
||||
private ulong[] m_h = new ulong[8];
|
||||
private ulong[] m_t = new ulong[2];
|
||||
private ulong[] m_f = new ulong[2];
|
||||
private byte[] m_buf = new byte[NbBlockBytes];
|
||||
private int m_cbBuf = 0;
|
||||
|
||||
private ulong[] m_m = new ulong[16];
|
||||
private ulong[] m_v = new ulong[16];
|
||||
|
||||
public int HashSize
|
||||
{
|
||||
get { return HashSizeValue; }
|
||||
}
|
||||
|
||||
public byte[] Hash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_bDisposed)
|
||||
throw new ObjectDisposedException(null);
|
||||
if (State != 0)
|
||||
throw new InvalidOperationException("Blak2B Cryptography Hash Not Yet Finalized");
|
||||
return (byte[])HashValue.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
public Blake2b()
|
||||
{
|
||||
m_cbHashLength = NbMaxOutBytes;
|
||||
this.HashSizeValue = NbMaxOutBytes * 8; // Bits
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public Blake2b(int cbHashLength)
|
||||
{
|
||||
if((cbHashLength < 0) || (cbHashLength > NbMaxOutBytes))
|
||||
throw new ArgumentOutOfRangeException("cbHashLength");
|
||||
|
||||
m_cbHashLength = cbHashLength;
|
||||
this.HashSizeValue = cbHashLength * 8; // Bits
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
Debug.Assert(m_h.Length == g_vIV.Length);
|
||||
Array.Copy(g_vIV, m_h, m_h.Length);
|
||||
|
||||
// Fan-out = 1, depth = 1
|
||||
m_h[0] ^= 0x0000000001010000UL ^ (ulong)m_cbHashLength;
|
||||
|
||||
Array.Clear(m_t, 0, m_t.Length);
|
||||
Array.Clear(m_f, 0, m_f.Length);
|
||||
Array.Clear(m_buf, 0, m_buf.Length);
|
||||
m_cbBuf = 0;
|
||||
|
||||
Array.Clear(m_m, 0, m_m.Length);
|
||||
Array.Clear(m_v, 0, m_v.Length);
|
||||
}
|
||||
|
||||
private static void G(ulong[] v, ulong[] m, int r16, int i,
|
||||
int a, int b, int c, int d)
|
||||
{
|
||||
int p = r16 + i;
|
||||
|
||||
v[a] += v[b] + m[g_vSigma[p]];
|
||||
v[d] = MemUtil.RotateRight64(v[d] ^ v[a], 32);
|
||||
v[c] += v[d];
|
||||
v[b] = MemUtil.RotateRight64(v[b] ^ v[c], 24);
|
||||
v[a] += v[b] + m[g_vSigma[p + 1]];
|
||||
v[d] = MemUtil.RotateRight64(v[d] ^ v[a], 16);
|
||||
v[c] += v[d];
|
||||
v[b] = MemUtil.RotateRight64(v[b] ^ v[c], 63);
|
||||
}
|
||||
|
||||
private void Compress(byte[] pb, int iOffset)
|
||||
{
|
||||
ulong[] v = m_v;
|
||||
ulong[] m = m_m;
|
||||
ulong[] h = m_h;
|
||||
|
||||
for(int i = 0; i < 16; ++i)
|
||||
m[i] = MemUtil.BytesToUInt64(pb, iOffset + (i << 3));
|
||||
|
||||
Array.Copy(h, v, 8);
|
||||
v[8] = g_vIV[0];
|
||||
v[9] = g_vIV[1];
|
||||
v[10] = g_vIV[2];
|
||||
v[11] = g_vIV[3];
|
||||
v[12] = g_vIV[4] ^ m_t[0];
|
||||
v[13] = g_vIV[5] ^ m_t[1];
|
||||
v[14] = g_vIV[6] ^ m_f[0];
|
||||
v[15] = g_vIV[7] ^ m_f[1];
|
||||
|
||||
for(int r = 0; r < NbRounds; ++r)
|
||||
{
|
||||
int r16 = r << 4;
|
||||
|
||||
G(v, m, r16, 0, 0, 4, 8, 12);
|
||||
G(v, m, r16, 2, 1, 5, 9, 13);
|
||||
G(v, m, r16, 4, 2, 6, 10, 14);
|
||||
G(v, m, r16, 6, 3, 7, 11, 15);
|
||||
G(v, m, r16, 8, 0, 5, 10, 15);
|
||||
G(v, m, r16, 10, 1, 6, 11, 12);
|
||||
G(v, m, r16, 12, 2, 7, 8, 13);
|
||||
G(v, m, r16, 14, 3, 4, 9, 14);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 8; ++i)
|
||||
h[i] ^= v[i] ^ v[i + 8];
|
||||
}
|
||||
|
||||
private void IncrementCounter(ulong cb)
|
||||
{
|
||||
m_t[0] += cb;
|
||||
if(m_t[0] < cb) ++m_t[1];
|
||||
}
|
||||
|
||||
private void HashCore(byte[] array, int ibStart, int cbSize)
|
||||
{
|
||||
Debug.Assert(m_f[0] == 0);
|
||||
|
||||
if((m_cbBuf + cbSize) > NbBlockBytes) // Not '>=' (buffer must not be empty)
|
||||
{
|
||||
int cbFill = NbBlockBytes - m_cbBuf;
|
||||
if(cbFill > 0) Array.Copy(array, ibStart, m_buf, m_cbBuf, cbFill);
|
||||
|
||||
IncrementCounter((ulong)NbBlockBytes);
|
||||
Compress(m_buf, 0);
|
||||
|
||||
m_cbBuf = 0;
|
||||
cbSize -= cbFill;
|
||||
ibStart += cbFill;
|
||||
|
||||
while(cbSize > NbBlockBytes) // Not '>=' (buffer must not be empty)
|
||||
{
|
||||
IncrementCounter((ulong)NbBlockBytes);
|
||||
Compress(array, ibStart);
|
||||
|
||||
cbSize -= NbBlockBytes;
|
||||
ibStart += NbBlockBytes;
|
||||
}
|
||||
}
|
||||
|
||||
if(cbSize > 0)
|
||||
{
|
||||
Debug.Assert((m_cbBuf + cbSize) <= NbBlockBytes);
|
||||
|
||||
Array.Copy(array, ibStart, m_buf, m_cbBuf, cbSize);
|
||||
m_cbBuf += cbSize;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] HashFinal()
|
||||
{
|
||||
if(m_f[0] != 0) { Debug.Assert(false); throw new InvalidOperationException(); }
|
||||
Debug.Assert(((m_t[1] == 0) && (m_t[0] == 0)) ||
|
||||
(m_cbBuf > 0)); // Buffer must not be empty for last block processing
|
||||
|
||||
m_f[0] = ulong.MaxValue; // Indicate last block
|
||||
|
||||
int cbFill = NbBlockBytes - m_cbBuf;
|
||||
if(cbFill > 0) Array.Clear(m_buf, m_cbBuf, cbFill);
|
||||
|
||||
IncrementCounter((ulong)m_cbBuf);
|
||||
Compress(m_buf, 0);
|
||||
|
||||
byte[] pbHash = new byte[NbMaxOutBytes];
|
||||
for(int i = 0; i < m_h.Length; ++i)
|
||||
MemUtil.UInt64ToBytesEx(m_h[i], pbHash, i << 3);
|
||||
|
||||
if(m_cbHashLength == NbMaxOutBytes) return pbHash;
|
||||
Debug.Assert(m_cbHashLength < NbMaxOutBytes);
|
||||
|
||||
byte[] pbShort = new byte[m_cbHashLength];
|
||||
if(m_cbHashLength > 0)
|
||||
Array.Copy(pbHash, pbShort, m_cbHashLength);
|
||||
MemUtil.ZeroByteArray(pbHash);
|
||||
return pbShort;
|
||||
}
|
||||
|
||||
public byte[] ComputeHash(Stream inputStream)
|
||||
{
|
||||
if (m_bDisposed)
|
||||
throw new ObjectDisposedException(null);
|
||||
|
||||
// Default the buffer size to 4K.
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
do
|
||||
{
|
||||
bytesRead = inputStream.Read(buffer, 0, 4096);
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
HashCore(buffer, 0, bytesRead);
|
||||
}
|
||||
} while (bytesRead > 0);
|
||||
|
||||
HashValue = HashFinal();
|
||||
byte[] Tmp = (byte[])HashValue.Clone();
|
||||
Initialize();
|
||||
return (Tmp);
|
||||
}
|
||||
|
||||
public byte[] ComputeHash(byte[] buffer)
|
||||
{
|
||||
if (m_bDisposed)
|
||||
throw new ObjectDisposedException(null);
|
||||
|
||||
// Do some validation
|
||||
if (buffer == null) throw new ArgumentNullException("buffer");
|
||||
|
||||
HashCore(buffer, 0, buffer.Length);
|
||||
HashValue = HashFinal();
|
||||
byte[] Tmp = (byte[])HashValue.Clone();
|
||||
Initialize();
|
||||
return (Tmp);
|
||||
}
|
||||
|
||||
public byte[] ComputeHash(byte[] buffer, int offset, int count)
|
||||
{
|
||||
// Do some validation
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException("offset", "ArgumentOutOfRange_NeedNonNegNum");
|
||||
if (count < 0 || (count > buffer.Length))
|
||||
throw new ArgumentException("Argument_InvalidValue");
|
||||
if ((buffer.Length - count) < offset)
|
||||
throw new ArgumentException("Argument_InvalidOffLen");
|
||||
|
||||
if (m_bDisposed)
|
||||
throw new ObjectDisposedException(null);
|
||||
|
||||
HashCore(buffer, offset, count);
|
||||
HashValue = HashFinal();
|
||||
byte[] Tmp = (byte[])HashValue.Clone();
|
||||
Initialize();
|
||||
return (Tmp);
|
||||
}
|
||||
|
||||
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
|
||||
{
|
||||
// Do some validation, we let BlockCopy do the destination array validation
|
||||
if (inputBuffer == null)
|
||||
throw new ArgumentNullException("inputBuffer");
|
||||
if (inputOffset < 0)
|
||||
throw new ArgumentOutOfRangeException("inputOffset", "ArgumentOutOfRange_NeedNonNegNum");
|
||||
if (inputCount < 0 || (inputCount > inputBuffer.Length))
|
||||
throw new ArgumentException("Argument_InvalidValue");
|
||||
if ((inputBuffer.Length - inputCount) < inputOffset)
|
||||
throw new ArgumentException("Argument_InvalidOffLen");
|
||||
|
||||
if (m_bDisposed)
|
||||
throw new ObjectDisposedException(null);
|
||||
|
||||
// Change the State value
|
||||
State = 1;
|
||||
HashCore(inputBuffer, inputOffset, inputCount);
|
||||
if ((outputBuffer != null) && ((inputBuffer != outputBuffer) || (inputOffset != outputOffset)))
|
||||
Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount);
|
||||
return inputCount;
|
||||
}
|
||||
|
||||
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
|
||||
{
|
||||
// Do some validation
|
||||
if (inputBuffer == null)
|
||||
throw new ArgumentNullException("inputBuffer");
|
||||
if (inputOffset < 0)
|
||||
throw new ArgumentOutOfRangeException("inputOffset", "ArgumentOutOfRange_NeedNonNegNum");
|
||||
if (inputCount < 0 || (inputCount > inputBuffer.Length))
|
||||
throw new ArgumentException("Argument_InvalidValue");
|
||||
if ((inputBuffer.Length - inputCount) < inputOffset)
|
||||
throw new ArgumentException("Argument_InvalidOffLen");
|
||||
|
||||
if (m_bDisposed)
|
||||
throw new ObjectDisposedException(null);
|
||||
|
||||
HashCore(inputBuffer, inputOffset, inputCount);
|
||||
HashValue = HashFinal();
|
||||
byte[] outputBytes;
|
||||
if (inputCount != 0)
|
||||
{
|
||||
outputBytes = new byte[inputCount];
|
||||
Array.Copy(inputBuffer, inputOffset, outputBytes, 0, inputCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputBytes = MemUtil.EmptyByteArray;
|
||||
}
|
||||
// reset the State value
|
||||
State = 0;
|
||||
return outputBytes;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
(this as IDisposable).Dispose();
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (HashValue != null)
|
||||
Array.Clear(HashValue, 0, HashValue.Length);
|
||||
HashValue = null;
|
||||
m_bDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
ModernKeePassLib/Cryptography/Hash/DigestManaged.cs
Normal file
31
ModernKeePassLib/Cryptography/Hash/DigestManaged.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Hash
|
||||
{
|
||||
public abstract class DigestManaged: IDisposable
|
||||
{
|
||||
protected IDigest Hash;
|
||||
|
||||
public byte[] ComputeHash(byte[] value)
|
||||
{
|
||||
return ComputeHash(value, 0, value.Length);
|
||||
}
|
||||
|
||||
public byte[] ComputeHash(byte[] value, int offset, int length)
|
||||
{
|
||||
if (value == null) throw new ArgumentNullException(nameof(value));
|
||||
|
||||
byte[] resBuf = new byte[Hash.GetDigestSize()];
|
||||
Hash.BlockUpdate(value, 0, length);
|
||||
Hash.DoFinal(resBuf, 0);
|
||||
|
||||
return resBuf;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Hash.Reset();
|
||||
}
|
||||
}
|
||||
}
|
31
ModernKeePassLib/Cryptography/Hash/HMAC.cs
Normal file
31
ModernKeePassLib/Cryptography/Hash/HMAC.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Hash
|
||||
{
|
||||
public class HMAC : IDisposable
|
||||
{
|
||||
protected HMac _hmac;
|
||||
|
||||
public byte[] ComputeHash(byte[] value)
|
||||
{
|
||||
return ComputeHash(value, 0, value.Length);
|
||||
}
|
||||
|
||||
public byte[] ComputeHash(byte[] value, int offset, int length)
|
||||
{
|
||||
if (value == null) throw new ArgumentNullException(nameof(value));
|
||||
|
||||
byte[] resBuf = new byte[_hmac.GetMacSize()];
|
||||
_hmac.BlockUpdate(value, 0, length);
|
||||
_hmac.DoFinal(resBuf, 0);
|
||||
|
||||
return resBuf;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_hmac.Reset();
|
||||
}
|
||||
}
|
||||
}
|
15
ModernKeePassLib/Cryptography/Hash/HMACSHA1.cs
Normal file
15
ModernKeePassLib/Cryptography/Hash/HMACSHA1.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Hash
|
||||
{
|
||||
public class HMACSHA1: HMAC
|
||||
{
|
||||
public HMACSHA1(byte[] key)
|
||||
{
|
||||
_hmac = new HMac(new Sha1Digest());
|
||||
_hmac.Init(new KeyParameter(key));
|
||||
}
|
||||
}
|
||||
}
|
30
ModernKeePassLib/Cryptography/Hash/HMACSHA256.cs
Normal file
30
ModernKeePassLib/Cryptography/Hash/HMACSHA256.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Hash
|
||||
{
|
||||
public class HMACSHA256: HMAC
|
||||
{
|
||||
public HMACSHA256(byte[] key)
|
||||
{
|
||||
_hmac = new HMac(new Sha256Digest());
|
||||
_hmac.Init(new KeyParameter(key));
|
||||
}
|
||||
|
||||
/*internal void TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
|
||||
{
|
||||
_hmac.BlockUpdate(inputBuffer, inputOffset, inputCount);
|
||||
}
|
||||
|
||||
internal void TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
|
||||
{
|
||||
_hmac.DoFinal(inputBuffer, inputOffset);
|
||||
}
|
||||
|
||||
internal void Initialize()
|
||||
{
|
||||
_hmac.Reset();
|
||||
}*/
|
||||
}
|
||||
}
|
12
ModernKeePassLib/Cryptography/Hash/SHA256Managed.cs
Normal file
12
ModernKeePassLib/Cryptography/Hash/SHA256Managed.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Hash
|
||||
{
|
||||
public class SHA256Managed : DigestManaged
|
||||
{
|
||||
public SHA256Managed()
|
||||
{
|
||||
Hash = new Sha256Digest();
|
||||
}
|
||||
}
|
||||
}
|
12
ModernKeePassLib/Cryptography/Hash/SHA512Managed.cs
Normal file
12
ModernKeePassLib/Cryptography/Hash/SHA512Managed.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Hash
|
||||
{
|
||||
public class SHA512Managed: DigestManaged
|
||||
{
|
||||
public SHA512Managed()
|
||||
{
|
||||
Hash = new Sha512Digest();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,31 +19,30 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
#if ModernKeePassLib
|
||||
using Windows.Security.Cryptography;
|
||||
using Windows.Security.Cryptography.Core;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
#else
|
||||
using Org.BouncyCastle.Crypto.Tls;
|
||||
#elif !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
using Org.BouncyCastle.Crypto.Tls;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public sealed class HashingStreamEx : Stream
|
||||
{
|
||||
private Stream m_sBaseStream;
|
||||
private bool m_bWriting;
|
||||
private readonly Stream m_sBaseStream;
|
||||
private readonly bool m_bWriting;
|
||||
#if ModernKeePassLib
|
||||
//private ICryptoTransform m_hash;
|
||||
//private CryptographicHash m_hash;
|
||||
private IDigest m_hash;
|
||||
private IDigest m_hash;
|
||||
#else
|
||||
private HashAlgorithm m_hash;
|
||||
#endif
|
||||
@@ -78,25 +77,21 @@ namespace ModernKeePassLib.Cryptography
|
||||
public override long Position
|
||||
{
|
||||
get { return m_sBaseStream.Position; }
|
||||
set { throw new NotSupportedException(); }
|
||||
set { Debug.Assert(false); throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
#if ModernKeePassLib
|
||||
//public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm? hashAlgorithm)
|
||||
public HashingStreamEx(Stream sBaseStream, bool bWriting, string hashAlgorithm)
|
||||
public HashingStreamEx(Stream sBaseStream, bool bWriting, IDigest hashAlgorithm)
|
||||
#else
|
||||
public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm)
|
||||
#endif
|
||||
{
|
||||
if(sBaseStream == null)
|
||||
throw new ArgumentNullException("sBaseStream");
|
||||
{
|
||||
if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
|
||||
|
||||
m_sBaseStream = sBaseStream;
|
||||
m_bWriting = bWriting;
|
||||
#if ModernKeePassLib
|
||||
//m_hash = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(hashAlgorithm ?? HashAlgorithm.Sha256).CreateHash();
|
||||
//m_hash = HashAlgorithmProvider.OpenAlgorithm(hashAlgorithm ?? HashAlgorithmNames.Sha256).CreateHash();
|
||||
m_hash = new Sha256Digest();
|
||||
m_hash = hashAlgorithm ?? new Sha256Digest();
|
||||
#elif !KeePassLibSD
|
||||
m_hash = (hashAlgorithm ?? new SHA256Managed());
|
||||
#else // KeePassLibSD
|
||||
@@ -107,17 +102,46 @@ namespace ModernKeePassLib.Cryptography
|
||||
try { if(m_hash == null) m_hash = HashAlgorithm.Create(); }
|
||||
catch(Exception) { }
|
||||
#endif
|
||||
if (m_hash == null) { Debug.Assert(false); return; }
|
||||
if(m_hash == null) { Debug.Assert(false); return; }
|
||||
|
||||
// Validate hash algorithm
|
||||
/*if((!m_hash.CanReuseTransform) || (!m_hash.CanTransformMultipleBlocks) ||
|
||||
(m_hash.InputBlockSize != 1) || (m_hash.OutputBlockSize != 1))
|
||||
#if ModernKeePassLib
|
||||
#else
|
||||
if(!m_hash.CanReuseTransform || !m_hash.CanTransformMultipleBlocks)
|
||||
{
|
||||
#if false && DEBUG
|
||||
MessageService.ShowWarning("Broken HashAlgorithm object in HashingStreamEx.");
|
||||
#endif
|
||||
Debug.Assert(false);
|
||||
m_hash = null;
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing)
|
||||
{
|
||||
if(m_hash != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
m_pbFinalHash = new byte[32];
|
||||
m_hash.DoFinal(m_pbFinalHash, 0);
|
||||
m_hash.Reset();
|
||||
#else
|
||||
m_hash.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
|
||||
|
||||
m_pbFinalHash = m_hash.Hash;
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
m_hash = null;
|
||||
}
|
||||
|
||||
m_sBaseStream.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
@@ -125,37 +149,6 @@ namespace ModernKeePassLib.Cryptography
|
||||
m_sBaseStream.Flush();
|
||||
}
|
||||
|
||||
#if ModernKeePassLib || KeePassRT
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(!disposing) return;
|
||||
#else
|
||||
public override void Close()
|
||||
{
|
||||
#endif
|
||||
if(m_hash != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
//m_hash.TransformFinalBlock(new byte[0], 0, 0);
|
||||
#if ModernKeePassLib
|
||||
//m_pbFinalHash = (m_hash as CryptographicHash).GetValueAndReset ();
|
||||
//CryptographicBuffer.CopyToByteArray(m_hash.GetValueAndReset(), out m_pbFinalHash);
|
||||
m_pbFinalHash = new byte[32];
|
||||
m_hash.DoFinal(m_pbFinalHash, 0);
|
||||
m_hash.Reset();
|
||||
#else
|
||||
m_pbFinalHash = m_hash.Hash;
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
m_hash = null;
|
||||
}
|
||||
|
||||
m_sBaseStream.Dispose();
|
||||
}
|
||||
|
||||
public override long Seek(long lOffset, SeekOrigin soOrigin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
@@ -185,12 +178,14 @@ namespace ModernKeePassLib.Cryptography
|
||||
#endif
|
||||
|
||||
if((m_hash != null) && (nRead > 0))
|
||||
//m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset);
|
||||
//m_hash.Append(CryptographicBuffer.CreateFromByteArray(pbBuffer));
|
||||
#if ModernKeePassLib
|
||||
m_hash.BlockUpdate(pbBuffer, nOffset, nRead);
|
||||
#else
|
||||
m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset);
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
#endif
|
||||
|
||||
return nRead;
|
||||
@@ -205,12 +200,15 @@ namespace ModernKeePassLib.Cryptography
|
||||
Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
|
||||
#endif
|
||||
|
||||
if ((m_hash != null) && (nCount > 0))
|
||||
//m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset);
|
||||
//m_hash.Append(CryptographicBuffer.CreateFromByteArray(pbBuffer));
|
||||
if((m_hash != null) && (nCount > 0))
|
||||
#if ModernKeePassLib
|
||||
m_hash.BlockUpdate(pbBuffer, nOffset, nCount);
|
||||
#else
|
||||
m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset);
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
#endif
|
||||
|
||||
m_sBaseStream.Write(pbBuffer, nOffset, nCount);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,18 +19,17 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
#if ModernKeePassLib
|
||||
using Windows.Security.Cryptography;
|
||||
#else
|
||||
using ModernKeePassLib.Cryptography.Hash;
|
||||
#elif !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
using System.Globalization;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
using Windows.Security.Cryptography.Core;
|
||||
|
||||
#if (!KeePassLibSD && !KeePassRT)
|
||||
#if !KeePassLibSD
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
@@ -45,22 +44,12 @@ namespace ModernKeePassLib.Cryptography
|
||||
uint uCodeDigits, bool bAddChecksum, int iTruncationOffset)
|
||||
{
|
||||
byte[] pbText = MemUtil.UInt64ToBytes(uFactor);
|
||||
Array.Reverse(pbText); // Big-Endian
|
||||
Array.Reverse(pbText); // To big-endian
|
||||
|
||||
#if ModernKeePassLib
|
||||
/*var hsha1 = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha1).CreateHash(pbSecret);
|
||||
hsha1.Append(pbText);
|
||||
var pbHash = hsha1.GetValueAndReset();*/
|
||||
var hsha1 = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1).CreateHash(CryptographicBuffer.CreateFromByteArray(pbSecret));
|
||||
hsha1.Append(CryptographicBuffer.CreateFromByteArray(pbText));
|
||||
byte[] pbHash;
|
||||
CryptographicBuffer.CopyToByteArray(hsha1.GetValueAndReset(), out pbHash);
|
||||
#else
|
||||
HMACSHA1 hsha1 = new HMACSHA1(pbSecret);
|
||||
byte[] pbHash = hsha1.ComputeHash(pbText);
|
||||
#endif
|
||||
|
||||
uint uOffset = (uint)(pbHash[pbHash.Length - 1] & 0xF);
|
||||
uint uOffset = (uint)(pbHash[pbHash.Length - 1] & 0xF);
|
||||
if((iTruncationOffset >= 0) && (iTruncationOffset < (pbHash.Length - 4)))
|
||||
uOffset = (uint)iTruncationOffset;
|
||||
|
||||
|
402
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.GCrypt.cs
Normal file
402
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.GCrypt.cs
Normal file
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
{
|
||||
public sealed partial class AesKdf : KdfEngine
|
||||
{
|
||||
private static bool TransformKeyGCrypt(byte[] pbData32, byte[] pbSeed32,
|
||||
ulong uRounds)
|
||||
{
|
||||
byte[] pbNewData32 = null;
|
||||
try
|
||||
{
|
||||
if(GCryptInitLib())
|
||||
{
|
||||
pbNewData32 = new byte[32];
|
||||
Array.Copy(pbData32, pbNewData32, 32);
|
||||
|
||||
if(TransformKeyGCryptPriv(pbNewData32, pbSeed32, uRounds))
|
||||
{
|
||||
Array.Copy(pbNewData32, pbData32, 32);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception) { }
|
||||
finally { if(pbNewData32 != null) MemUtil.ZeroByteArray(pbNewData32); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TransformKeyBenchmarkGCrypt(uint uTimeMs, out ulong uRounds)
|
||||
{
|
||||
uRounds = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if(GCryptInitLib())
|
||||
return TransformKeyBenchmarkGCryptPriv(uTimeMs, ref uRounds);
|
||||
}
|
||||
catch(Exception) { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool GCryptInitLib()
|
||||
{
|
||||
Debug.Assert(Marshal.SizeOf(typeof(int)) == 4); // Also on 64-bit systems
|
||||
Debug.Assert(Marshal.SizeOf(typeof(uint)) == 4);
|
||||
|
||||
if(!NativeLib.IsUnix()) return false; // Independent of workaround state
|
||||
if(!MonoWorkarounds.IsRequired(1468)) return false; // Can be turned off
|
||||
|
||||
// gcry_check_version initializes the library;
|
||||
// throws when LibGCrypt is not available
|
||||
NativeMethods.gcry_check_version(IntPtr.Zero);
|
||||
return true;
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// Multi-threaded implementation
|
||||
|
||||
// For some reason, the following multi-threaded implementation
|
||||
// is slower than the single-threaded implementation below
|
||||
// (threading overhead by Mono? LibGCrypt threading issues?)
|
||||
/* private sealed class GCryptTransformInfo : IDisposable
|
||||
{
|
||||
public IntPtr Data16;
|
||||
public IntPtr Seed32;
|
||||
public ulong Rounds;
|
||||
public uint TimeMs;
|
||||
|
||||
public bool Success = false;
|
||||
|
||||
public GCryptTransformInfo(byte[] pbData32, int iDataOffset,
|
||||
byte[] pbSeed32, ulong uRounds, uint uTimeMs)
|
||||
{
|
||||
this.Data16 = Marshal.AllocCoTaskMem(16);
|
||||
Marshal.Copy(pbData32, iDataOffset, this.Data16, 16);
|
||||
|
||||
this.Seed32 = Marshal.AllocCoTaskMem(32);
|
||||
Marshal.Copy(pbSeed32, 0, this.Seed32, 32);
|
||||
|
||||
this.Rounds = uRounds;
|
||||
this.TimeMs = uTimeMs;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if(this.Data16 != IntPtr.Zero)
|
||||
{
|
||||
Marshal.WriteInt64(this.Data16, 0);
|
||||
Marshal.WriteInt64(this.Data16, 8, 0);
|
||||
Marshal.FreeCoTaskMem(this.Data16);
|
||||
this.Data16 = IntPtr.Zero;
|
||||
}
|
||||
|
||||
if(this.Seed32 != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(this.Seed32);
|
||||
this.Seed32 = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static GCryptTransformInfo[] GCryptRun(byte[] pbData32,
|
||||
byte[] pbSeed32, ulong uRounds, uint uTimeMs, ParameterizedThreadStart fL,
|
||||
ParameterizedThreadStart fR)
|
||||
{
|
||||
GCryptTransformInfo tiL = new GCryptTransformInfo(pbData32, 0,
|
||||
pbSeed32, uRounds, uTimeMs);
|
||||
GCryptTransformInfo tiR = new GCryptTransformInfo(pbData32, 16,
|
||||
pbSeed32, uRounds, uTimeMs);
|
||||
|
||||
Thread th = new Thread(fL);
|
||||
th.Start(tiL);
|
||||
|
||||
fR(tiR);
|
||||
|
||||
th.Join();
|
||||
|
||||
Marshal.Copy(tiL.Data16, pbData32, 0, 16);
|
||||
Marshal.Copy(tiR.Data16, pbData32, 16, 16);
|
||||
|
||||
tiL.Dispose();
|
||||
tiR.Dispose();
|
||||
|
||||
if(tiL.Success && tiR.Success)
|
||||
return new GCryptTransformInfo[2] { tiL, tiR };
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool TransformKeyGCryptPriv(byte[] pbData32, byte[] pbSeed32,
|
||||
ulong uRounds)
|
||||
{
|
||||
return (GCryptRun(pbData32, pbSeed32, uRounds, 0,
|
||||
new ParameterizedThreadStart(AesKdf.GCryptTransformTh),
|
||||
new ParameterizedThreadStart(AesKdf.GCryptTransformTh)) != null);
|
||||
}
|
||||
|
||||
private static bool GCryptInitCipher(ref IntPtr h, GCryptTransformInfo ti)
|
||||
{
|
||||
NativeMethods.gcry_cipher_open(ref h, NativeMethods.GCRY_CIPHER_AES256,
|
||||
NativeMethods.GCRY_CIPHER_MODE_ECB, 0);
|
||||
if(h == IntPtr.Zero) { Debug.Assert(false); return false; }
|
||||
|
||||
IntPtr n32 = new IntPtr(32);
|
||||
if(NativeMethods.gcry_cipher_setkey(h, ti.Seed32, n32) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void GCryptTransformTh(object o)
|
||||
{
|
||||
IntPtr h = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
GCryptTransformInfo ti = (o as GCryptTransformInfo);
|
||||
if(ti == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(!GCryptInitCipher(ref h, ti)) return;
|
||||
|
||||
IntPtr n16 = new IntPtr(16);
|
||||
for(ulong u = 0; u < ti.Rounds; ++u)
|
||||
{
|
||||
if(NativeMethods.gcry_cipher_encrypt(h, ti.Data16, n16,
|
||||
IntPtr.Zero, IntPtr.Zero) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ti.Success = true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally
|
||||
{
|
||||
try { if(h != IntPtr.Zero) NativeMethods.gcry_cipher_close(h); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TransformKeyBenchmarkGCryptPriv(uint uTimeMs, ref ulong uRounds)
|
||||
{
|
||||
GCryptTransformInfo[] v = GCryptRun(new byte[32], new byte[32],
|
||||
0, uTimeMs,
|
||||
new ParameterizedThreadStart(AesKdf.GCryptBenchmarkTh),
|
||||
new ParameterizedThreadStart(AesKdf.GCryptBenchmarkTh));
|
||||
|
||||
if(v != null)
|
||||
{
|
||||
ulong uL = Math.Min(v[0].Rounds, ulong.MaxValue >> 1);
|
||||
ulong uR = Math.Min(v[1].Rounds, ulong.MaxValue >> 1);
|
||||
uRounds = (uL + uR) / 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void GCryptBenchmarkTh(object o)
|
||||
{
|
||||
IntPtr h = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
GCryptTransformInfo ti = (o as GCryptTransformInfo);
|
||||
if(ti == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(!GCryptInitCipher(ref h, ti)) return;
|
||||
|
||||
ulong r = 0;
|
||||
IntPtr n16 = new IntPtr(16);
|
||||
int tStart = Environment.TickCount;
|
||||
while(true)
|
||||
{
|
||||
for(ulong j = 0; j < BenchStep; ++j)
|
||||
{
|
||||
if(NativeMethods.gcry_cipher_encrypt(h, ti.Data16, n16,
|
||||
IntPtr.Zero, IntPtr.Zero) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
r += BenchStep;
|
||||
if(r < BenchStep) // Overflow check
|
||||
{
|
||||
r = ulong.MaxValue;
|
||||
break;
|
||||
}
|
||||
|
||||
uint tElapsed = (uint)(Environment.TickCount - tStart);
|
||||
if(tElapsed > ti.TimeMs) break;
|
||||
}
|
||||
|
||||
ti.Rounds = r;
|
||||
ti.Success = true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally
|
||||
{
|
||||
try { if(h != IntPtr.Zero) NativeMethods.gcry_cipher_close(h); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
} */
|
||||
|
||||
// =============================================================
|
||||
// Single-threaded implementation
|
||||
|
||||
private static bool GCryptInitCipher(ref IntPtr h, IntPtr pSeed32)
|
||||
{
|
||||
NativeMethods.gcry_cipher_open(ref h, NativeMethods.GCRY_CIPHER_AES256,
|
||||
NativeMethods.GCRY_CIPHER_MODE_ECB, 0);
|
||||
if(h == IntPtr.Zero) { Debug.Assert(false); return false; }
|
||||
|
||||
IntPtr n32 = new IntPtr(32);
|
||||
if(NativeMethods.gcry_cipher_setkey(h, pSeed32, n32) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool GCryptBegin(byte[] pbData32, byte[] pbSeed32,
|
||||
ref IntPtr h, ref IntPtr pData32, ref IntPtr pSeed32)
|
||||
{
|
||||
pData32 = Marshal.AllocCoTaskMem(32);
|
||||
pSeed32 = Marshal.AllocCoTaskMem(32);
|
||||
|
||||
Marshal.Copy(pbData32, 0, pData32, 32);
|
||||
Marshal.Copy(pbSeed32, 0, pSeed32, 32);
|
||||
|
||||
return GCryptInitCipher(ref h, pSeed32);
|
||||
}
|
||||
|
||||
private static void GCryptEnd(IntPtr h, IntPtr pData32, IntPtr pSeed32)
|
||||
{
|
||||
NativeMethods.gcry_cipher_close(h);
|
||||
|
||||
Marshal.WriteInt64(pData32, 0);
|
||||
Marshal.WriteInt64(pData32, 8, 0);
|
||||
Marshal.WriteInt64(pData32, 16, 0);
|
||||
Marshal.WriteInt64(pData32, 24, 0);
|
||||
|
||||
Marshal.FreeCoTaskMem(pData32);
|
||||
Marshal.FreeCoTaskMem(pSeed32);
|
||||
}
|
||||
|
||||
private static bool TransformKeyGCryptPriv(byte[] pbData32, byte[] pbSeed32,
|
||||
ulong uRounds)
|
||||
{
|
||||
IntPtr h = IntPtr.Zero, pData32 = IntPtr.Zero, pSeed32 = IntPtr.Zero;
|
||||
if(!GCryptBegin(pbData32, pbSeed32, ref h, ref pData32, ref pSeed32))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
IntPtr n32 = new IntPtr(32);
|
||||
for(ulong i = 0; i < uRounds; ++i)
|
||||
{
|
||||
if(NativeMethods.gcry_cipher_encrypt(h, pData32, n32,
|
||||
IntPtr.Zero, IntPtr.Zero) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Marshal.Copy(pData32, pbData32, 0, 32);
|
||||
return true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally { GCryptEnd(h, pData32, pSeed32); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TransformKeyBenchmarkGCryptPriv(uint uTimeMs, ref ulong uRounds)
|
||||
{
|
||||
byte[] pbData32 = new byte[32];
|
||||
byte[] pbSeed32 = new byte[32];
|
||||
|
||||
IntPtr h = IntPtr.Zero, pData32 = IntPtr.Zero, pSeed32 = IntPtr.Zero;
|
||||
if(!GCryptBegin(pbData32, pbSeed32, ref h, ref pData32, ref pSeed32))
|
||||
return false;
|
||||
|
||||
uint uMaxMs = uTimeMs;
|
||||
ulong uDiv = 1;
|
||||
if(uMaxMs <= (uint.MaxValue >> 1)) { uMaxMs *= 2U; uDiv = 2; }
|
||||
|
||||
try
|
||||
{
|
||||
ulong r = 0;
|
||||
IntPtr n32 = new IntPtr(32);
|
||||
int tStart = Environment.TickCount;
|
||||
while(true)
|
||||
{
|
||||
for(ulong j = 0; j < BenchStep; ++j)
|
||||
{
|
||||
if(NativeMethods.gcry_cipher_encrypt(h, pData32, n32,
|
||||
IntPtr.Zero, IntPtr.Zero) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
r += BenchStep;
|
||||
if(r < BenchStep) // Overflow check
|
||||
{
|
||||
r = ulong.MaxValue;
|
||||
break;
|
||||
}
|
||||
|
||||
uint tElapsed = (uint)(Environment.TickCount - tStart);
|
||||
if(tElapsed > uMaxMs) break;
|
||||
}
|
||||
|
||||
uRounds = r / uDiv;
|
||||
return true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally { GCryptEnd(h, pData32, pSeed32); }
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
277
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs
Normal file
277
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Engines;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
{
|
||||
public sealed partial class AesKdf : KdfEngine
|
||||
{
|
||||
private static readonly PwUuid g_uuid = new PwUuid(new byte[] {
|
||||
0xC9, 0xD9, 0xF3, 0x9A, 0x62, 0x8A, 0x44, 0x60,
|
||||
0xBF, 0x74, 0x0D, 0x08, 0xC1, 0x8A, 0x4F, 0xEA });
|
||||
|
||||
public const string ParamRounds = "R"; // UInt64
|
||||
public const string ParamSeed = "S"; // Byte[32]
|
||||
|
||||
private const ulong BenchStep = 3001;
|
||||
|
||||
public override PwUuid Uuid
|
||||
{
|
||||
get { return g_uuid; }
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return "AES-KDF"; }
|
||||
}
|
||||
|
||||
public AesKdf()
|
||||
{
|
||||
}
|
||||
|
||||
public override KdfParameters GetDefaultParameters()
|
||||
{
|
||||
KdfParameters p = base.GetDefaultParameters();
|
||||
p.SetUInt64(ParamRounds, PwDefs.DefaultKeyEncryptionRounds);
|
||||
return p;
|
||||
}
|
||||
|
||||
public override void Randomize(KdfParameters p)
|
||||
{
|
||||
if(p == null) { Debug.Assert(false); return; }
|
||||
Debug.Assert(g_uuid.Equals(p.KdfUuid));
|
||||
|
||||
byte[] pbSeed = CryptoRandom.Instance.GetRandomBytes(32);
|
||||
p.SetByteArray(ParamSeed, pbSeed);
|
||||
}
|
||||
|
||||
public override byte[] Transform(byte[] pbMsg, KdfParameters p)
|
||||
{
|
||||
if(pbMsg == null) throw new ArgumentNullException("pbMsg");
|
||||
if(p == null) throw new ArgumentNullException("p");
|
||||
|
||||
Type tRounds = p.GetTypeOf(ParamRounds);
|
||||
if(tRounds == null) throw new ArgumentNullException("p.Rounds");
|
||||
if(tRounds != typeof(ulong)) throw new ArgumentOutOfRangeException("p.Rounds");
|
||||
ulong uRounds = p.GetUInt64(ParamRounds, 0);
|
||||
|
||||
byte[] pbSeed = p.GetByteArray(ParamSeed);
|
||||
if(pbSeed == null) throw new ArgumentNullException("p.Seed");
|
||||
|
||||
if(pbMsg.Length != 32)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
pbMsg = CryptoUtil.HashSha256(pbMsg);
|
||||
}
|
||||
|
||||
if(pbSeed.Length != 32)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
pbSeed = CryptoUtil.HashSha256(pbSeed);
|
||||
}
|
||||
|
||||
return TransformKey(pbMsg, pbSeed, uRounds);
|
||||
}
|
||||
|
||||
private static byte[] TransformKey(byte[] pbOriginalKey32, byte[] pbKeySeed32,
|
||||
ulong uNumRounds)
|
||||
{
|
||||
Debug.Assert((pbOriginalKey32 != null) && (pbOriginalKey32.Length == 32));
|
||||
if(pbOriginalKey32 == null) throw new ArgumentNullException("pbOriginalKey32");
|
||||
if(pbOriginalKey32.Length != 32) throw new ArgumentException();
|
||||
|
||||
Debug.Assert((pbKeySeed32 != null) && (pbKeySeed32.Length == 32));
|
||||
if(pbKeySeed32 == null) throw new ArgumentNullException("pbKeySeed32");
|
||||
if(pbKeySeed32.Length != 32) throw new ArgumentException();
|
||||
|
||||
byte[] pbNewKey = new byte[32];
|
||||
Array.Copy(pbOriginalKey32, pbNewKey, pbNewKey.Length);
|
||||
|
||||
try
|
||||
{
|
||||
#if !ModernKeePassLib
|
||||
// Try to use the native library first
|
||||
if(NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds))
|
||||
return CryptoUtil.HashSha256(pbNewKey);
|
||||
#endif
|
||||
|
||||
if(TransformKeyGCrypt(pbNewKey, pbKeySeed32, uNumRounds))
|
||||
return CryptoUtil.HashSha256(pbNewKey);
|
||||
|
||||
if(TransformKeyManaged(pbNewKey, pbKeySeed32, uNumRounds))
|
||||
return CryptoUtil.HashSha256(pbNewKey);
|
||||
}
|
||||
finally { MemUtil.ZeroByteArray(pbNewKey); }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
|
||||
ulong uNumRounds)
|
||||
{
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
KeyParameter kp = new KeyParameter(pbKeySeed32);
|
||||
AesEngine aes = new AesEngine();
|
||||
aes.Init(true, kp);
|
||||
|
||||
for(ulong i = 0; i < uNumRounds; ++i)
|
||||
{
|
||||
aes.ProcessBlock(pbNewKey32, 0, pbNewKey32, 0);
|
||||
aes.ProcessBlock(pbNewKey32, 16, pbNewKey32, 16);
|
||||
}
|
||||
#else
|
||||
byte[] pbIV = new byte[16];
|
||||
Array.Clear(pbIV, 0, pbIV.Length);
|
||||
|
||||
SymmetricAlgorithm a = CryptoUtil.CreateAes();
|
||||
if(a.BlockSize != 128) // AES block size
|
||||
{
|
||||
Debug.Assert(false);
|
||||
a.BlockSize = 128;
|
||||
}
|
||||
|
||||
a.IV = pbIV;
|
||||
a.Mode = CipherMode.ECB;
|
||||
a.KeySize = 256;
|
||||
a.Key = pbKeySeed32;
|
||||
ICryptoTransform iCrypt = a.CreateEncryptor();
|
||||
|
||||
// !iCrypt.CanReuseTransform -- doesn't work with Mono
|
||||
if((iCrypt == null) || (iCrypt.InputBlockSize != 16) ||
|
||||
(iCrypt.OutputBlockSize != 16))
|
||||
{
|
||||
Debug.Assert(false, "Invalid ICryptoTransform.");
|
||||
Debug.Assert((iCrypt.InputBlockSize == 16), "Invalid input block size!");
|
||||
Debug.Assert((iCrypt.OutputBlockSize == 16), "Invalid output block size!");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(ulong i = 0; i < uNumRounds; ++i)
|
||||
{
|
||||
iCrypt.TransformBlock(pbNewKey32, 0, 16, pbNewKey32, 0);
|
||||
iCrypt.TransformBlock(pbNewKey32, 16, 16, pbNewKey32, 16);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override KdfParameters GetBestParameters(uint uMilliseconds)
|
||||
{
|
||||
KdfParameters p = GetDefaultParameters();
|
||||
ulong uRounds;
|
||||
#if !ModernKeePassLib
|
||||
// Try native method
|
||||
if(NativeLib.TransformKeyBenchmark256(uMilliseconds, out uRounds))
|
||||
{
|
||||
p.SetUInt64(ParamRounds, uRounds);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
if(TransformKeyBenchmarkGCrypt(uMilliseconds, out uRounds))
|
||||
{
|
||||
p.SetUInt64(ParamRounds, uRounds);
|
||||
return p;
|
||||
}
|
||||
|
||||
byte[] pbKey = new byte[32];
|
||||
byte[] pbNewKey = new byte[32];
|
||||
for(int i = 0; i < pbKey.Length; ++i)
|
||||
{
|
||||
pbKey[i] = (byte)i;
|
||||
pbNewKey[i] = (byte)i;
|
||||
}
|
||||
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
KeyParameter kp = new KeyParameter(pbKey);
|
||||
AesEngine aes = new AesEngine();
|
||||
aes.Init(true, kp);
|
||||
#else
|
||||
byte[] pbIV = new byte[16];
|
||||
Array.Clear(pbIV, 0, pbIV.Length);
|
||||
|
||||
SymmetricAlgorithm a = CryptoUtil.CreateAes();
|
||||
if(a.BlockSize != 128) // AES block size
|
||||
{
|
||||
Debug.Assert(false);
|
||||
a.BlockSize = 128;
|
||||
}
|
||||
|
||||
a.IV = pbIV;
|
||||
a.Mode = CipherMode.ECB;
|
||||
a.KeySize = 256;
|
||||
a.Key = pbKey;
|
||||
ICryptoTransform iCrypt = a.CreateEncryptor();
|
||||
|
||||
// !iCrypt.CanReuseTransform -- doesn't work with Mono
|
||||
if((iCrypt == null) || (iCrypt.InputBlockSize != 16) ||
|
||||
(iCrypt.OutputBlockSize != 16))
|
||||
{
|
||||
Debug.Assert(false, "Invalid ICryptoTransform.");
|
||||
Debug.Assert(iCrypt.InputBlockSize == 16, "Invalid input block size!");
|
||||
Debug.Assert(iCrypt.OutputBlockSize == 16, "Invalid output block size!");
|
||||
|
||||
p.SetUInt64(ParamRounds, PwDefs.DefaultKeyEncryptionRounds);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
uRounds = 0;
|
||||
int tStart = Environment.TickCount;
|
||||
while(true)
|
||||
{
|
||||
for(ulong j = 0; j < BenchStep; ++j)
|
||||
{
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
aes.ProcessBlock(pbNewKey, 0, pbNewKey, 0);
|
||||
aes.ProcessBlock(pbNewKey, 16, pbNewKey, 16);
|
||||
#else
|
||||
iCrypt.TransformBlock(pbNewKey, 0, 16, pbNewKey, 0);
|
||||
iCrypt.TransformBlock(pbNewKey, 16, 16, pbNewKey, 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
uRounds += BenchStep;
|
||||
if(uRounds < BenchStep) // Overflow check
|
||||
{
|
||||
uRounds = ulong.MaxValue;
|
||||
break;
|
||||
}
|
||||
|
||||
uint tElapsed = (uint)(Environment.TickCount - tStart);
|
||||
if(tElapsed > uMilliseconds) break;
|
||||
}
|
||||
|
||||
p.SetUInt64(ParamRounds, uRounds);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
633
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs
Normal file
633
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs
Normal file
@@ -0,0 +1,633 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
// This implementation is based on the official reference C
|
||||
// implementation by Daniel Dinu and Dmitry Khovratovich (CC0 1.0).
|
||||
|
||||
// Relative iterations (* = B2ROUND_ARRAYS \\ G_INLINED):
|
||||
// * | false true
|
||||
// ------+-----------
|
||||
// false | 8885 9618
|
||||
// true | 9009 9636
|
||||
#define ARGON2_B2ROUND_ARRAYS
|
||||
#define ARGON2_G_INLINED
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using ModernKeePassLib.Cryptography.Hash;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
{
|
||||
public sealed partial class Argon2Kdf : KdfEngine
|
||||
{
|
||||
private const ulong NbBlockSize = 1024;
|
||||
private const ulong NbBlockSizeInQW = NbBlockSize / 8UL;
|
||||
private const ulong NbSyncPoints = 4;
|
||||
|
||||
private const int NbPreHashDigestLength = 64;
|
||||
private const int NbPreHashSeedLength = NbPreHashDigestLength + 8;
|
||||
|
||||
#if ARGON2_B2ROUND_ARRAYS
|
||||
private static int[][] g_vFBCols = null;
|
||||
private static int[][] g_vFBRows = null;
|
||||
#endif
|
||||
|
||||
private sealed class Argon2Ctx
|
||||
{
|
||||
public uint Version = 0;
|
||||
|
||||
public ulong Lanes = 0;
|
||||
public ulong TCost = 0;
|
||||
public ulong MCost = 0;
|
||||
public ulong MemoryBlocks = 0;
|
||||
public ulong SegmentLength = 0;
|
||||
public ulong LaneLength = 0;
|
||||
|
||||
public ulong[] Mem = null;
|
||||
}
|
||||
|
||||
private sealed class Argon2ThreadInfo
|
||||
{
|
||||
public Argon2Ctx Context = null;
|
||||
public ManualResetEvent Finished = new ManualResetEvent(false);
|
||||
|
||||
public ulong Pass = 0;
|
||||
public ulong Lane = 0;
|
||||
public ulong Slice = 0;
|
||||
public ulong Index = 0;
|
||||
|
||||
public void Release()
|
||||
{
|
||||
if(this.Finished != null)
|
||||
{
|
||||
this.Finished.Dispose();
|
||||
this.Finished = null;
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] Argon2d(byte[] pbMsg, byte[] pbSalt, uint uParallel,
|
||||
ulong uMem, ulong uIt, int cbOut, uint uVersion, byte[] pbSecretKey,
|
||||
byte[] pbAssocData)
|
||||
{
|
||||
pbSecretKey = (pbSecretKey ?? MemUtil.EmptyByteArray);
|
||||
pbAssocData = (pbAssocData ?? MemUtil.EmptyByteArray);
|
||||
|
||||
#if ARGON2_B2ROUND_ARRAYS
|
||||
InitB2RoundIndexArrays();
|
||||
#endif
|
||||
|
||||
Argon2Ctx ctx = new Argon2Ctx();
|
||||
ctx.Version = uVersion;
|
||||
|
||||
ctx.Lanes = uParallel;
|
||||
ctx.TCost = uIt;
|
||||
ctx.MCost = uMem / NbBlockSize;
|
||||
ctx.MemoryBlocks = Math.Max(ctx.MCost, 2UL * NbSyncPoints * ctx.Lanes);
|
||||
|
||||
ctx.SegmentLength = ctx.MemoryBlocks / (ctx.Lanes * NbSyncPoints);
|
||||
ctx.MemoryBlocks = ctx.SegmentLength * ctx.Lanes * NbSyncPoints;
|
||||
|
||||
ctx.LaneLength = ctx.SegmentLength * NbSyncPoints;
|
||||
|
||||
Debug.Assert(NbBlockSize == (NbBlockSizeInQW *
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
(ulong)Marshal.SizeOf<ulong>()
|
||||
#else
|
||||
(ulong)Marshal.SizeOf(typeof(ulong))
|
||||
#endif
|
||||
));
|
||||
ctx.Mem = new ulong[ctx.MemoryBlocks * NbBlockSizeInQW];
|
||||
|
||||
Blake2b h = new Blake2b();
|
||||
|
||||
// Initial hash
|
||||
Debug.Assert(h.HashSize == (NbPreHashDigestLength * 8));
|
||||
byte[] pbBuf = new byte[4];
|
||||
MemUtil.UInt32ToBytesEx(uParallel, pbBuf, 0);
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
MemUtil.UInt32ToBytesEx((uint)cbOut, pbBuf, 0);
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
MemUtil.UInt32ToBytesEx((uint)ctx.MCost, pbBuf, 0);
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
MemUtil.UInt32ToBytesEx((uint)uIt, pbBuf, 0);
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
MemUtil.UInt32ToBytesEx(uVersion, pbBuf, 0);
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
MemUtil.UInt32ToBytesEx(0, pbBuf, 0); // Argon2d type = 0
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
MemUtil.UInt32ToBytesEx((uint)pbMsg.Length, pbBuf, 0);
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
h.TransformBlock(pbMsg, 0, pbMsg.Length, pbMsg, 0);
|
||||
MemUtil.UInt32ToBytesEx((uint)pbSalt.Length, pbBuf, 0);
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
h.TransformBlock(pbSalt, 0, pbSalt.Length, pbSalt, 0);
|
||||
MemUtil.UInt32ToBytesEx((uint)pbSecretKey.Length, pbBuf, 0);
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
h.TransformBlock(pbSecretKey, 0, pbSecretKey.Length, pbSecretKey, 0);
|
||||
MemUtil.UInt32ToBytesEx((uint)pbAssocData.Length, pbBuf, 0);
|
||||
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
|
||||
h.TransformBlock(pbAssocData, 0, pbAssocData.Length, pbAssocData, 0);
|
||||
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
|
||||
byte[] pbH0 = h.Hash;
|
||||
Debug.Assert(pbH0.Length == 64);
|
||||
|
||||
byte[] pbBlockHash = new byte[NbPreHashSeedLength];
|
||||
Array.Copy(pbH0, pbBlockHash, pbH0.Length);
|
||||
MemUtil.ZeroByteArray(pbH0);
|
||||
|
||||
FillFirstBlocks(ctx, pbBlockHash, h);
|
||||
MemUtil.ZeroByteArray(pbBlockHash);
|
||||
|
||||
FillMemoryBlocks(ctx);
|
||||
|
||||
byte[] pbOut = FinalHash(ctx, cbOut, h);
|
||||
|
||||
h.Clear();
|
||||
MemUtil.ZeroArray<ulong>(ctx.Mem);
|
||||
return pbOut;
|
||||
}
|
||||
|
||||
private static void LoadBlock(ulong[] pqDst, ulong uDstOffset, byte[] pbIn)
|
||||
{
|
||||
// for(ulong i = 0; i < NbBlockSizeInQW; ++i)
|
||||
// pqDst[uDstOffset + i] = MemUtil.BytesToUInt64(pbIn, (int)(i << 3));
|
||||
|
||||
Debug.Assert((uDstOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
|
||||
int iDstOffset = (int)uDstOffset;
|
||||
for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
|
||||
pqDst[iDstOffset + i] = MemUtil.BytesToUInt64(pbIn, i << 3);
|
||||
}
|
||||
|
||||
private static void StoreBlock(byte[] pbDst, ulong[] pqSrc)
|
||||
{
|
||||
for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
|
||||
MemUtil.UInt64ToBytesEx(pqSrc[i], pbDst, i << 3);
|
||||
}
|
||||
|
||||
private static void CopyBlock(ulong[] vDst, ulong uDstOffset, ulong[] vSrc,
|
||||
ulong uSrcOffset)
|
||||
{
|
||||
// for(ulong i = 0; i < NbBlockSizeInQW; ++i)
|
||||
// vDst[uDstOffset + i] = vSrc[uSrcOffset + i];
|
||||
|
||||
// Debug.Assert((uDstOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
|
||||
// Debug.Assert((uSrcOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
|
||||
// int iDstOffset = (int)uDstOffset;
|
||||
// int iSrcOffset = (int)uSrcOffset;
|
||||
// for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
|
||||
// vDst[iDstOffset + i] = vSrc[iSrcOffset + i];
|
||||
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
Array.Copy(vSrc, (int)uSrcOffset, vDst, (int)uDstOffset,
|
||||
(int)NbBlockSizeInQW);
|
||||
#else
|
||||
Array.Copy(vSrc, (long)uSrcOffset, vDst, (long)uDstOffset,
|
||||
(long)NbBlockSizeInQW);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void XorBlock(ulong[] vDst, ulong uDstOffset, ulong[] vSrc,
|
||||
ulong uSrcOffset)
|
||||
{
|
||||
// for(ulong i = 0; i < NbBlockSizeInQW; ++i)
|
||||
// vDst[uDstOffset + i] ^= vSrc[uSrcOffset + i];
|
||||
|
||||
Debug.Assert((uDstOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
|
||||
Debug.Assert((uSrcOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
|
||||
int iDstOffset = (int)uDstOffset;
|
||||
int iSrcOffset = (int)uSrcOffset;
|
||||
for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
|
||||
vDst[iDstOffset + i] ^= vSrc[iSrcOffset + i];
|
||||
}
|
||||
|
||||
private static void Blake2bLong(byte[] pbOut, int cbOut,
|
||||
byte[] pbIn, int cbIn, Blake2b h)
|
||||
{
|
||||
Debug.Assert((h != null) && (h.HashSize == (64 * 8)));
|
||||
|
||||
byte[] pbOutLen = new byte[4];
|
||||
MemUtil.UInt32ToBytesEx((uint)cbOut, pbOutLen, 0);
|
||||
|
||||
if(cbOut <= 64)
|
||||
{
|
||||
Blake2b hOut = ((cbOut == 64) ? h : new Blake2b(cbOut));
|
||||
if(cbOut == 64) hOut.Initialize();
|
||||
|
||||
hOut.TransformBlock(pbOutLen, 0, pbOutLen.Length, pbOutLen, 0);
|
||||
hOut.TransformBlock(pbIn, 0, cbIn, pbIn, 0);
|
||||
hOut.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
|
||||
|
||||
Array.Copy(hOut.Hash, pbOut, cbOut);
|
||||
|
||||
if(cbOut < 64) hOut.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
h.Initialize();
|
||||
h.TransformBlock(pbOutLen, 0, pbOutLen.Length, pbOutLen, 0);
|
||||
h.TransformBlock(pbIn, 0, cbIn, pbIn, 0);
|
||||
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
|
||||
|
||||
byte[] pbOutBuffer = new byte[64];
|
||||
Array.Copy(h.Hash, pbOutBuffer, pbOutBuffer.Length);
|
||||
|
||||
int ibOut = 64 / 2;
|
||||
Array.Copy(pbOutBuffer, pbOut, ibOut);
|
||||
int cbToProduce = cbOut - ibOut;
|
||||
|
||||
h.Initialize();
|
||||
while(cbToProduce > 64)
|
||||
{
|
||||
byte[] pbHash = h.ComputeHash(pbOutBuffer);
|
||||
Array.Copy(pbHash, pbOutBuffer, 64);
|
||||
|
||||
Array.Copy(pbHash, 0, pbOut, ibOut, 64 / 2);
|
||||
ibOut += 64 / 2;
|
||||
cbToProduce -= 64 / 2;
|
||||
|
||||
MemUtil.ZeroByteArray(pbHash);
|
||||
}
|
||||
|
||||
using(Blake2b hOut = new Blake2b(cbToProduce))
|
||||
{
|
||||
byte[] pbHash = hOut.ComputeHash(pbOutBuffer);
|
||||
Array.Copy(pbHash, 0, pbOut, ibOut, cbToProduce);
|
||||
|
||||
MemUtil.ZeroByteArray(pbHash);
|
||||
}
|
||||
|
||||
MemUtil.ZeroByteArray(pbOutBuffer);
|
||||
}
|
||||
|
||||
#if !ARGON2_G_INLINED
|
||||
private static ulong BlaMka(ulong x, ulong y)
|
||||
{
|
||||
ulong xy = (x & 0xFFFFFFFFUL) * (y & 0xFFFFFFFFUL);
|
||||
return (x + y + (xy << 1));
|
||||
}
|
||||
|
||||
private static void G(ulong[] v, int a, int b, int c, int d)
|
||||
{
|
||||
ulong va = v[a], vb = v[b], vc = v[c], vd = v[d];
|
||||
|
||||
va = BlaMka(va, vb);
|
||||
vd = MemUtil.RotateRight64(vd ^ va, 32);
|
||||
vc = BlaMka(vc, vd);
|
||||
vb = MemUtil.RotateRight64(vb ^ vc, 24);
|
||||
va = BlaMka(va, vb);
|
||||
vd = MemUtil.RotateRight64(vd ^ va, 16);
|
||||
vc = BlaMka(vc, vd);
|
||||
vb = MemUtil.RotateRight64(vb ^ vc, 63);
|
||||
|
||||
v[a] = va;
|
||||
v[b] = vb;
|
||||
v[c] = vc;
|
||||
v[d] = vd;
|
||||
}
|
||||
#else
|
||||
private static void G(ulong[] v, int a, int b, int c, int d)
|
||||
{
|
||||
ulong va = v[a], vb = v[b], vc = v[c], vd = v[d];
|
||||
|
||||
ulong xy = (va & 0xFFFFFFFFUL) * (vb & 0xFFFFFFFFUL);
|
||||
va += vb + (xy << 1);
|
||||
|
||||
vd = MemUtil.RotateRight64(vd ^ va, 32);
|
||||
|
||||
xy = (vc & 0xFFFFFFFFUL) * (vd & 0xFFFFFFFFUL);
|
||||
vc += vd + (xy << 1);
|
||||
|
||||
vb = MemUtil.RotateRight64(vb ^ vc, 24);
|
||||
|
||||
xy = (va & 0xFFFFFFFFUL) * (vb & 0xFFFFFFFFUL);
|
||||
va += vb + (xy << 1);
|
||||
|
||||
vd = MemUtil.RotateRight64(vd ^ va, 16);
|
||||
|
||||
xy = (vc & 0xFFFFFFFFUL) * (vd & 0xFFFFFFFFUL);
|
||||
vc += vd + (xy << 1);
|
||||
|
||||
vb = MemUtil.RotateRight64(vb ^ vc, 63);
|
||||
|
||||
v[a] = va;
|
||||
v[b] = vb;
|
||||
v[c] = vc;
|
||||
v[d] = vd;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ARGON2_B2ROUND_ARRAYS
|
||||
private static void Blake2RoundNoMsg(ulong[] pbR, int[] v)
|
||||
{
|
||||
G(pbR, v[0], v[4], v[8], v[12]);
|
||||
G(pbR, v[1], v[5], v[9], v[13]);
|
||||
G(pbR, v[2], v[6], v[10], v[14]);
|
||||
G(pbR, v[3], v[7], v[11], v[15]);
|
||||
G(pbR, v[0], v[5], v[10], v[15]);
|
||||
G(pbR, v[1], v[6], v[11], v[12]);
|
||||
G(pbR, v[2], v[7], v[8], v[13]);
|
||||
G(pbR, v[3], v[4], v[9], v[14]);
|
||||
}
|
||||
#else
|
||||
private static void Blake2RoundNoMsgCols16i(ulong[] pbR, int i)
|
||||
{
|
||||
G(pbR, i, i + 4, i + 8, i + 12);
|
||||
G(pbR, i + 1, i + 5, i + 9, i + 13);
|
||||
G(pbR, i + 2, i + 6, i + 10, i + 14);
|
||||
G(pbR, i + 3, i + 7, i + 11, i + 15);
|
||||
G(pbR, i, i + 5, i + 10, i + 15);
|
||||
G(pbR, i + 1, i + 6, i + 11, i + 12);
|
||||
G(pbR, i + 2, i + 7, i + 8, i + 13);
|
||||
G(pbR, i + 3, i + 4, i + 9, i + 14);
|
||||
}
|
||||
|
||||
private static void Blake2RoundNoMsgRows2i(ulong[] pbR, int i)
|
||||
{
|
||||
G(pbR, i, i + 32, i + 64, i + 96);
|
||||
G(pbR, i + 1, i + 33, i + 65, i + 97);
|
||||
G(pbR, i + 16, i + 48, i + 80, i + 112);
|
||||
G(pbR, i + 17, i + 49, i + 81, i + 113);
|
||||
G(pbR, i, i + 33, i + 80, i + 113);
|
||||
G(pbR, i + 1, i + 48, i + 81, i + 96);
|
||||
G(pbR, i + 16, i + 49, i + 64, i + 97);
|
||||
G(pbR, i + 17, i + 32, i + 65, i + 112);
|
||||
}
|
||||
#endif
|
||||
|
||||
private static void FillFirstBlocks(Argon2Ctx ctx, byte[] pbBlockHash,
|
||||
Blake2b h)
|
||||
{
|
||||
byte[] pbBlock = new byte[NbBlockSize];
|
||||
|
||||
for(ulong l = 0; l < ctx.Lanes; ++l)
|
||||
{
|
||||
MemUtil.UInt32ToBytesEx(0, pbBlockHash, NbPreHashDigestLength);
|
||||
MemUtil.UInt32ToBytesEx((uint)l, pbBlockHash, NbPreHashDigestLength + 4);
|
||||
|
||||
Blake2bLong(pbBlock, (int)NbBlockSize, pbBlockHash,
|
||||
NbPreHashSeedLength, h);
|
||||
LoadBlock(ctx.Mem, l * ctx.LaneLength * NbBlockSizeInQW, pbBlock);
|
||||
|
||||
MemUtil.UInt32ToBytesEx(1, pbBlockHash, NbPreHashDigestLength);
|
||||
|
||||
Blake2bLong(pbBlock, (int)NbBlockSize, pbBlockHash,
|
||||
NbPreHashSeedLength, h);
|
||||
LoadBlock(ctx.Mem, (l * ctx.LaneLength + 1UL) * NbBlockSizeInQW, pbBlock);
|
||||
}
|
||||
|
||||
MemUtil.ZeroByteArray(pbBlock);
|
||||
}
|
||||
|
||||
private static ulong IndexAlpha(Argon2Ctx ctx, Argon2ThreadInfo ti,
|
||||
uint uPseudoRand, bool bSameLane)
|
||||
{
|
||||
ulong uRefAreaSize;
|
||||
if(ti.Pass == 0)
|
||||
{
|
||||
if(ti.Slice == 0)
|
||||
{
|
||||
Debug.Assert(ti.Index > 0);
|
||||
uRefAreaSize = ti.Index - 1UL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(bSameLane)
|
||||
uRefAreaSize = ti.Slice * ctx.SegmentLength +
|
||||
ti.Index - 1UL;
|
||||
else
|
||||
uRefAreaSize = ti.Slice * ctx.SegmentLength -
|
||||
((ti.Index == 0UL) ? 1UL : 0UL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(bSameLane)
|
||||
uRefAreaSize = ctx.LaneLength - ctx.SegmentLength +
|
||||
ti.Index - 1UL;
|
||||
else
|
||||
uRefAreaSize = ctx.LaneLength - ctx.SegmentLength -
|
||||
((ti.Index == 0) ? 1UL : 0UL);
|
||||
}
|
||||
Debug.Assert(uRefAreaSize <= (ulong)uint.MaxValue);
|
||||
|
||||
ulong uRelPos = uPseudoRand;
|
||||
uRelPos = (uRelPos * uRelPos) >> 32;
|
||||
uRelPos = uRefAreaSize - 1UL - ((uRefAreaSize * uRelPos) >> 32);
|
||||
|
||||
ulong uStart = 0;
|
||||
if(ti.Pass != 0)
|
||||
uStart = (((ti.Slice + 1UL) == NbSyncPoints) ? 0UL :
|
||||
((ti.Slice + 1UL) * ctx.SegmentLength));
|
||||
Debug.Assert(uStart <= (ulong)uint.MaxValue);
|
||||
|
||||
Debug.Assert(ctx.LaneLength <= (ulong)uint.MaxValue);
|
||||
return ((uStart + uRelPos) % ctx.LaneLength);
|
||||
}
|
||||
|
||||
private static void FillMemoryBlocks(Argon2Ctx ctx)
|
||||
{
|
||||
int np = (int)ctx.Lanes;
|
||||
Argon2ThreadInfo[] v = new Argon2ThreadInfo[np];
|
||||
|
||||
for(ulong r = 0; r < ctx.TCost; ++r)
|
||||
{
|
||||
for(ulong s = 0; s < NbSyncPoints; ++s)
|
||||
{
|
||||
for(int l = 0; l < np; ++l)
|
||||
{
|
||||
Argon2ThreadInfo ti = new Argon2ThreadInfo();
|
||||
ti.Context = ctx;
|
||||
|
||||
ti.Pass = r;
|
||||
ti.Lane = (ulong)l;
|
||||
ti.Slice = s;
|
||||
#if !ModernKeePassLib
|
||||
if(!ThreadPool.QueueUserWorkItem(FillSegmentThr, ti))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
#endif
|
||||
v[l] = ti;
|
||||
}
|
||||
|
||||
for(int l = 0; l < np; ++l)
|
||||
{
|
||||
v[l].Finished.WaitOne();
|
||||
v[l].Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void FillSegmentThr(object o)
|
||||
{
|
||||
Argon2ThreadInfo ti = (o as Argon2ThreadInfo);
|
||||
if(ti == null) { Debug.Assert(false); return; }
|
||||
|
||||
try
|
||||
{
|
||||
Argon2Ctx ctx = ti.Context;
|
||||
if(ctx == null) { Debug.Assert(false); return; }
|
||||
|
||||
Debug.Assert(ctx.Version >= MinVersion);
|
||||
bool bCanXor = (ctx.Version >= 0x13U);
|
||||
|
||||
ulong uStart = 0;
|
||||
if((ti.Pass == 0) && (ti.Slice == 0)) uStart = 2;
|
||||
|
||||
ulong uCur = (ti.Lane * ctx.LaneLength) + (ti.Slice *
|
||||
ctx.SegmentLength) + uStart;
|
||||
|
||||
ulong uPrev = (((uCur % ctx.LaneLength) == 0) ?
|
||||
(uCur + ctx.LaneLength - 1UL) : (uCur - 1UL));
|
||||
|
||||
ulong[] pbR = new ulong[NbBlockSizeInQW];
|
||||
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
|
||||
|
||||
for(ulong i = uStart; i < ctx.SegmentLength; ++i)
|
||||
{
|
||||
if((uCur % ctx.LaneLength) == 1)
|
||||
uPrev = uCur - 1UL;
|
||||
|
||||
ulong uPseudoRand = ctx.Mem[uPrev * NbBlockSizeInQW];
|
||||
ulong uRefLane = (uPseudoRand >> 32) % ctx.Lanes;
|
||||
if((ti.Pass == 0) && (ti.Slice == 0))
|
||||
uRefLane = ti.Lane;
|
||||
|
||||
ti.Index = i;
|
||||
ulong uRefIndex = IndexAlpha(ctx, ti, (uint)uPseudoRand,
|
||||
(uRefLane == ti.Lane));
|
||||
|
||||
ulong uRefBlockIndex = (ctx.LaneLength * uRefLane +
|
||||
uRefIndex) * NbBlockSizeInQW;
|
||||
ulong uCurBlockIndex = uCur * NbBlockSizeInQW;
|
||||
|
||||
FillBlock(ctx.Mem, uPrev * NbBlockSizeInQW, uRefBlockIndex,
|
||||
uCurBlockIndex, ((ti.Pass != 0) && bCanXor), pbR, pbTmp);
|
||||
|
||||
++uCur;
|
||||
++uPrev;
|
||||
}
|
||||
|
||||
MemUtil.ZeroArray<ulong>(pbR);
|
||||
MemUtil.ZeroArray<ulong>(pbTmp);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try { ti.Finished.Set(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
#if ARGON2_B2ROUND_ARRAYS
|
||||
private static void InitB2RoundIndexArrays()
|
||||
{
|
||||
int[][] vCols = g_vFBCols;
|
||||
if(vCols == null)
|
||||
{
|
||||
vCols = new int[8][];
|
||||
Debug.Assert(vCols.Length == 8);
|
||||
int e = 0;
|
||||
for(int i = 0; i < 8; ++i)
|
||||
{
|
||||
vCols[i] = new int[16];
|
||||
for(int j = 0; j < 16; ++j)
|
||||
{
|
||||
vCols[i][j] = e;
|
||||
++e;
|
||||
}
|
||||
}
|
||||
|
||||
g_vFBCols = vCols;
|
||||
}
|
||||
|
||||
int[][] vRows = g_vFBRows;
|
||||
if(vRows == null)
|
||||
{
|
||||
vRows = new int[8][];
|
||||
for(int i = 0; i < 8; ++i)
|
||||
{
|
||||
vRows[i] = new int[16];
|
||||
for(int j = 0; j < 16; ++j)
|
||||
{
|
||||
int jh = j / 2;
|
||||
vRows[i][j] = (2 * i) + (16 * jh) + (j & 1);
|
||||
}
|
||||
}
|
||||
|
||||
g_vFBRows = vRows;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private static void FillBlock(ulong[] pMem, ulong uPrev, ulong uRef,
|
||||
ulong uNext, bool bXor, ulong[] pbR, ulong[] pbTmp)
|
||||
{
|
||||
CopyBlock(pbR, 0, pMem, uRef);
|
||||
XorBlock(pbR, 0, pMem, uPrev);
|
||||
CopyBlock(pbTmp, 0, pbR, 0);
|
||||
if(bXor) XorBlock(pbTmp, 0, pMem, uNext);
|
||||
|
||||
#if ARGON2_B2ROUND_ARRAYS
|
||||
int[][] vCols = g_vFBCols;
|
||||
int[][] vRows = g_vFBRows;
|
||||
for(int i = 0; i < 8; ++i)
|
||||
Blake2RoundNoMsg(pbR, vCols[i]);
|
||||
for(int i = 0; i < 8; ++i)
|
||||
Blake2RoundNoMsg(pbR, vRows[i]);
|
||||
#else
|
||||
for(int i = 0; i < (8 * 16); i += 16)
|
||||
Blake2RoundNoMsgCols16i(pbR, i);
|
||||
for(int i = 0; i < (8 * 2); i += 2)
|
||||
Blake2RoundNoMsgRows2i(pbR, i);
|
||||
#endif
|
||||
|
||||
CopyBlock(pMem, uNext, pbTmp, 0);
|
||||
XorBlock(pMem, uNext, pbR, 0);
|
||||
}
|
||||
|
||||
private static byte[] FinalHash(Argon2Ctx ctx, int cbOut, Blake2b h)
|
||||
{
|
||||
ulong[] pqBlockHash = new ulong[NbBlockSizeInQW];
|
||||
CopyBlock(pqBlockHash, 0, ctx.Mem, (ctx.LaneLength - 1UL) *
|
||||
NbBlockSizeInQW);
|
||||
for(ulong l = 1; l < ctx.Lanes; ++l)
|
||||
XorBlock(pqBlockHash, 0, ctx.Mem, (l * ctx.LaneLength +
|
||||
ctx.LaneLength - 1UL) * NbBlockSizeInQW);
|
||||
|
||||
byte[] pbBlockHashBytes = new byte[NbBlockSize];
|
||||
StoreBlock(pbBlockHashBytes, pqBlockHash);
|
||||
|
||||
byte[] pbOut = new byte[cbOut];
|
||||
Blake2bLong(pbOut, cbOut, pbBlockHashBytes, (int)NbBlockSize, h);
|
||||
|
||||
MemUtil.ZeroArray<ulong>(pqBlockHash);
|
||||
MemUtil.ZeroByteArray(pbBlockHashBytes);
|
||||
return pbOut;
|
||||
}
|
||||
}
|
||||
}
|
144
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.cs
Normal file
144
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
{
|
||||
public sealed partial class Argon2Kdf : KdfEngine
|
||||
{
|
||||
private static readonly PwUuid g_uuid = new PwUuid(new byte[] {
|
||||
0xEF, 0x63, 0x6D, 0xDF, 0x8C, 0x29, 0x44, 0x4B,
|
||||
0x91, 0xF7, 0xA9, 0xA4, 0x03, 0xE3, 0x0A, 0x0C });
|
||||
|
||||
public const string ParamSalt = "S"; // Byte[]
|
||||
public const string ParamParallelism = "P"; // UInt32
|
||||
public const string ParamMemory = "M"; // UInt64
|
||||
public const string ParamIterations = "I"; // UInt64
|
||||
public const string ParamVersion = "V"; // UInt32
|
||||
public const string ParamSecretKey = "K"; // Byte[]
|
||||
public const string ParamAssocData = "A"; // Byte[]
|
||||
|
||||
private const uint MinVersion = 0x10;
|
||||
private const uint MaxVersion = 0x13;
|
||||
|
||||
private const int MinSalt = 8;
|
||||
private const int MaxSalt = int.MaxValue; // .NET limit; 2^32 - 1 in spec
|
||||
|
||||
internal const ulong MinIterations = 1;
|
||||
internal const ulong MaxIterations = uint.MaxValue;
|
||||
|
||||
internal const ulong MinMemory = 1024 * 8; // For parallelism = 1
|
||||
// internal const ulong MaxMemory = (ulong)uint.MaxValue * 1024UL; // Spec
|
||||
internal const ulong MaxMemory = int.MaxValue; // .NET limit
|
||||
|
||||
internal const uint MinParallelism = 1;
|
||||
internal const uint MaxParallelism = (1 << 24) - 1;
|
||||
|
||||
internal const ulong DefaultIterations = 2;
|
||||
internal const ulong DefaultMemory = 1024 * 1024; // 1 MB
|
||||
internal const uint DefaultParallelism = 2;
|
||||
|
||||
public override PwUuid Uuid
|
||||
{
|
||||
get { return g_uuid; }
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return "Argon2"; }
|
||||
}
|
||||
|
||||
public Argon2Kdf()
|
||||
{
|
||||
}
|
||||
|
||||
public override KdfParameters GetDefaultParameters()
|
||||
{
|
||||
KdfParameters p = base.GetDefaultParameters();
|
||||
|
||||
p.SetUInt32(ParamVersion, MaxVersion);
|
||||
|
||||
p.SetUInt64(ParamIterations, DefaultIterations);
|
||||
p.SetUInt64(ParamMemory, DefaultMemory);
|
||||
p.SetUInt32(ParamParallelism, DefaultParallelism);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
public override void Randomize(KdfParameters p)
|
||||
{
|
||||
if(p == null) { Debug.Assert(false); return; }
|
||||
Debug.Assert(g_uuid.Equals(p.KdfUuid));
|
||||
|
||||
byte[] pb = CryptoRandom.Instance.GetRandomBytes(32);
|
||||
p.SetByteArray(ParamSalt, pb);
|
||||
}
|
||||
|
||||
public override byte[] Transform(byte[] pbMsg, KdfParameters p)
|
||||
{
|
||||
if(pbMsg == null) throw new ArgumentNullException("pbMsg");
|
||||
if(p == null) throw new ArgumentNullException("p");
|
||||
|
||||
byte[] pbSalt = p.GetByteArray(ParamSalt);
|
||||
if(pbSalt == null)
|
||||
throw new ArgumentNullException("p.Salt");
|
||||
if((pbSalt.Length < MinSalt) || (pbSalt.Length > MaxSalt))
|
||||
throw new ArgumentOutOfRangeException("p.Salt");
|
||||
|
||||
uint uPar = p.GetUInt32(ParamParallelism, 0);
|
||||
if((uPar < MinParallelism) || (uPar > MaxParallelism))
|
||||
throw new ArgumentOutOfRangeException("p.Parallelism");
|
||||
|
||||
ulong uMem = p.GetUInt64(ParamMemory, 0);
|
||||
if((uMem < MinMemory) || (uMem > MaxMemory))
|
||||
throw new ArgumentOutOfRangeException("p.Memory");
|
||||
|
||||
ulong uIt = p.GetUInt64(ParamIterations, 0);
|
||||
if((uIt < MinIterations) || (uIt > MaxIterations))
|
||||
throw new ArgumentOutOfRangeException("p.Iterations");
|
||||
|
||||
uint v = p.GetUInt32(ParamVersion, 0);
|
||||
if((v < MinVersion) || (v > MaxVersion))
|
||||
throw new ArgumentOutOfRangeException("p.Version");
|
||||
|
||||
byte[] pbSecretKey = p.GetByteArray(ParamSecretKey);
|
||||
byte[] pbAssocData = p.GetByteArray(ParamAssocData);
|
||||
|
||||
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
|
||||
32, v, pbSecretKey, pbAssocData);
|
||||
|
||||
if(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
|
||||
return pbRet;
|
||||
}
|
||||
|
||||
public override KdfParameters GetBestParameters(uint uMilliseconds)
|
||||
{
|
||||
KdfParameters p = GetDefaultParameters();
|
||||
Randomize(p);
|
||||
|
||||
MaximizeParamUInt64(p, ParamIterations, MinIterations,
|
||||
MaxIterations, uMilliseconds, true);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
142
ModernKeePassLib/Cryptography/KeyDerivation/KdfEngine.cs
Normal file
142
ModernKeePassLib/Cryptography/KeyDerivation/KdfEngine.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
{
|
||||
public abstract class KdfEngine
|
||||
{
|
||||
public abstract PwUuid Uuid
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public virtual KdfParameters GetDefaultParameters()
|
||||
{
|
||||
return new KdfParameters(this.Uuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate random seeds and store them in <paramref name="p" />.
|
||||
/// </summary>
|
||||
public virtual void Randomize(KdfParameters p)
|
||||
{
|
||||
Debug.Assert(p != null);
|
||||
Debug.Assert(p.KdfUuid.Equals(this.Uuid));
|
||||
}
|
||||
|
||||
public abstract byte[] Transform(byte[] pbMsg, KdfParameters p);
|
||||
|
||||
public virtual KdfParameters GetBestParameters(uint uMilliseconds)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected void MaximizeParamUInt64(KdfParameters p, string strName,
|
||||
ulong uMin, ulong uMax, uint uMilliseconds, bool bInterpSearch)
|
||||
{
|
||||
if(p == null) { Debug.Assert(false); return; }
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
|
||||
if(uMin > uMax) { Debug.Assert(false); return; }
|
||||
|
||||
if(uMax > (ulong.MaxValue >> 1))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
uMax = ulong.MaxValue >> 1;
|
||||
|
||||
if(uMin > uMax) { p.SetUInt64(strName, uMin); return; }
|
||||
}
|
||||
|
||||
byte[] pbMsg = new byte[32];
|
||||
for(int i = 0; i < pbMsg.Length; ++i) pbMsg[i] = (byte)i;
|
||||
|
||||
ulong uLow = uMin;
|
||||
ulong uHigh = uMin + 1UL;
|
||||
long tLow = 0;
|
||||
long tHigh = 0;
|
||||
long tTarget = (long)uMilliseconds;
|
||||
|
||||
// Determine range
|
||||
while(uHigh <= uMax)
|
||||
{
|
||||
p.SetUInt64(strName, uHigh);
|
||||
|
||||
// GC.Collect();
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
Transform(pbMsg, p);
|
||||
sw.Stop();
|
||||
|
||||
tHigh = sw.ElapsedMilliseconds;
|
||||
if(tHigh > tTarget) break;
|
||||
|
||||
uLow = uHigh;
|
||||
tLow = tHigh;
|
||||
uHigh <<= 1;
|
||||
}
|
||||
if(uHigh > uMax) { uHigh = uMax; tHigh = 0; }
|
||||
if(uLow > uHigh) uLow = uHigh; // Skips to end
|
||||
|
||||
// Find optimal number of iterations
|
||||
while((uHigh - uLow) >= 2UL)
|
||||
{
|
||||
ulong u = (uHigh + uLow) >> 1; // Binary search
|
||||
// Interpolation search, if possible
|
||||
if(bInterpSearch && (tLow > 0) && (tHigh > tTarget) &&
|
||||
(tLow <= tTarget))
|
||||
{
|
||||
u = uLow + (((uHigh - uLow) * (ulong)(tTarget - tLow)) /
|
||||
(ulong)(tHigh - tLow));
|
||||
if((u >= uLow) && (u <= uHigh))
|
||||
{
|
||||
u = Math.Max(u, uLow + 1UL);
|
||||
u = Math.Min(u, uHigh - 1UL);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false);
|
||||
u = (uHigh + uLow) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
p.SetUInt64(strName, u);
|
||||
|
||||
// GC.Collect();
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
Transform(pbMsg, p);
|
||||
sw.Stop();
|
||||
|
||||
long t = sw.ElapsedMilliseconds;
|
||||
if(t == tTarget) { uLow = u; break; }
|
||||
else if(t > tTarget) { uHigh = u; tHigh = t; }
|
||||
else { uLow = u; tLow = t; }
|
||||
}
|
||||
|
||||
p.SetUInt64(strName, uLow);
|
||||
}
|
||||
}
|
||||
}
|
80
ModernKeePassLib/Cryptography/KeyDerivation/KdfParameters.cs
Normal file
80
ModernKeePassLib/Cryptography/KeyDerivation/KdfParameters.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
{
|
||||
public sealed class KdfParameters : VariantDictionary
|
||||
{
|
||||
private const string ParamUuid = @"$UUID";
|
||||
|
||||
private readonly PwUuid m_puKdf;
|
||||
public PwUuid KdfUuid
|
||||
{
|
||||
get { return m_puKdf; }
|
||||
}
|
||||
|
||||
public KdfParameters(PwUuid puKdf)
|
||||
{
|
||||
if(puKdf == null) throw new ArgumentNullException("puKdf");
|
||||
|
||||
m_puKdf = puKdf;
|
||||
SetByteArray(ParamUuid, puKdf.UuidBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsupported.
|
||||
/// </summary>
|
||||
public override object Clone()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public static byte[] SerializeExt(KdfParameters p)
|
||||
{
|
||||
return VariantDictionary.Serialize(p);
|
||||
}
|
||||
|
||||
public static KdfParameters DeserializeExt(byte[] pb)
|
||||
{
|
||||
VariantDictionary d = VariantDictionary.Deserialize(pb);
|
||||
if(d == null) { Debug.Assert(false); return null; }
|
||||
|
||||
byte[] pbUuid = d.GetByteArray(ParamUuid);
|
||||
if((pbUuid == null) || (pbUuid.Length != (int)PwUuid.UuidSize))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
PwUuid pu = new PwUuid(pbUuid);
|
||||
KdfParameters p = new KdfParameters(pu);
|
||||
d.CopyTo(p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user