34 Commits
V1.3 ... V1.5

Author SHA1 Message Date
bg45
fa3d38db18 Hidden Settings button for now
Improved Welcome and About pages layout
Redone GroupDetail page with group list view outside of entries grid view
2017-11-08 14:42:45 +01:00
dd1e128e17 Improved design in Main Page sub pages titles 2017-11-08 14:42:45 +01:00
d247aa8334 WIP Layout improvements - bis
Group and Entry pages now have better navigation bar
Hamburger menu redesigned
2017-11-08 14:42:45 +01:00
5f995a10c1 WIP layout enhancments
MainPage list view now has left indicator (more modern)
2017-11-08 14:42:45 +01:00
bg45
b89998c053 Small refactor in MainPage 2017-11-08 14:42:45 +01:00
d10f617910 Major changes in MainPage: now is layout dependant (with a base, to prepare for Settings)
Main sub pages also changed to better work with Main (but needs improvement)
Added a Settings Page stub
2017-11-08 14:42:44 +01:00
1b439a4960 ModernKeePassLib implements HMAC correctly
Blake2b also implemented, but not tested
ModernKeePass app better implements focus on database password box (but still not working correctly)
2017-11-08 14:42:44 +01:00
bg45
5cf4ff3012 Set same width for groups in Zoomed Out view 2017-11-08 14:42:44 +01:00
46c8429cde Lots of code refactoring to use XAML behaviors instead of code-behind
New Save button in the AppBar
EntryPage now uses the same AppBar as GroupPage (but not shared...)
Some new Symbol mappings
2017-11-08 14:42:44 +01:00
8cd3801897 Bigger database password textbox
Wrong password sets the password box border to red
Typing text removes the error
Implemented real command bar at the bottom in Groups
Search box is always present
2017-11-08 14:42:44 +01:00
b47d7fb69b Use latest Nuget package 2017-11-08 14:42:44 +01:00
52e08d8c98 Code cleanup in KeePassLib 2017-11-08 14:42:44 +01:00
ad0d8d6c97 KeePassLib 2.37 tentatively working!!
Replaced WinRt hash providers with BouncyCastle in CryptoUtil
2017-11-08 14:42:43 +01:00
5b31d3ff72 WIP Lib 2.37 - databases created by ModernKeepass work fine, but no interoperability... 2017-11-08 14:42:43 +01:00
2bbd931b1a Hashblockstream test now works 2017-11-08 14:42:43 +01:00
8c09d266e8 KeePassLib selftest class corrected in release 2017-11-08 14:42:43 +01:00
a11d209280 Correct implementation of SHA512 with WinRT in KeepassLib 2017-11-08 14:42:43 +01:00
bg45
e95e62f184 WIP 2.37 - ter 2017-11-08 14:42:43 +01:00
bg45
84e7afc819 WIP - Implementing HMAC with BouncyCastle 2017-11-08 14:42:43 +01:00
bg45
c82d6d001d KeePassLib bumped to 2.37, compilation ok.
WIP tests
2017-11-08 14:42:42 +01:00
d5b7845242 WIP Update lib to 2.37 2017-11-08 14:42:42 +01:00
9de9ae54da WIP About page auto version 2017-11-08 14:42:42 +01:00
6548d1d9c1 Added DataTrigger on generate password button to close flyout 2017-11-08 14:42:42 +01:00
54ad395d13 Added password complexity indicator to Entry page 2017-11-08 14:42:42 +01:00
6bb37d9e70 Removed QueryString.Net package and replaced it with Data.Json
Version 1.4
2017-11-08 14:42:42 +01:00
66fd87124b Entries now have working expiration dates
About page redone (with working hyperlink)
WIP on how to display that info on the group detail page
2017-11-08 14:42:41 +01:00
7bb78fd374 WIP Entry expiration dates 2017-11-08 14:42:41 +01:00
dc62cedb74 Toast notifications and undo mechanism now works! (for group and entries) 2017-11-08 14:42:41 +01:00
bg45
bc6e7d0097 WIP Toasts - bis 2017-11-08 14:42:41 +01:00
01ed1bc9c1 WIP toast notifications
WIP layout and color changes
2017-11-08 14:42:41 +01:00
bg45
19008cdad2 Change password reveal button style in Entry page 2017-11-08 14:42:41 +01:00
8a312bec71 Implemented password generator in Entry form 2017-11-08 14:42:41 +01:00
2b8d37057c Added password complexity indicator on new database
Database password control now inside a Border element
Save page also has a title
2017-11-08 14:42:41 +01:00
5497e6fc00 Bug correction when canceling open file dialog
New About page
Main menu pages now have titles
2017-11-08 14:42:40 +01:00
175 changed files with 13388 additions and 3160 deletions

View File

@@ -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)));
}
}
}

View File

@@ -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>

View File

@@ -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 (e is LaunchActivatedEventArgs)
{
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(MainPage), e.Arguments);
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();
}
}
}

View File

@@ -1,4 +1,4 @@
MainPackage=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.3.0.17_AnyCPU.appx
SymbolPackage=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\AppPackages\ModernKeePass_1.3.0.17_Test\ModernKeePass_1.3.0.17_AnyCPU.appxsym
ResourcePack=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.3.0.17_scale-140.appx
ResourcePack=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.3.0.17_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

View File

@@ -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();

View 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();
}
}
}

View 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();
}
}
}

View File

@@ -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">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
<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,7 +337,13 @@
</Style>
</UserControl.Resources>
<StackPanel>
<PasswordBox x:Name="PasswordBox" Width="300" IsPasswordRevealButtonEnabled="True" KeyDown="PasswordBox_KeyDown" PlaceholderText="Password" Style="{StaticResource PasswordBoxWithButtonStyle}"/>
<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>

View File

@@ -1,7 +1,5 @@
using System;
using System.Threading.Tasks;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using ModernKeePass.Common;
@@ -23,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()
{
@@ -41,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)
else
{
Task.Factory.StartNew(
() => Dispatcher.RunAsync(CoreDispatcherPriority.Low,
() => PasswordBox.Focus(FocusState.Programmatic)));
VisualStateManager.GoToState(PasswordBox, "Normal", true);
StatusTextBlock.Text = string.Empty;
}
}
}
}

View File

@@ -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();
}
}
}

View 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();
}
}
}

View 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();
}
}
}

View 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();
}
}
}

View File

@@ -0,0 +1,7 @@
namespace ModernKeePass.Interfaces
{
public interface IHasSelectableObject
{
ISelectableModel SelectedItem { get; set; }
}
}

View 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();
}
}

View File

@@ -0,0 +1,7 @@
namespace ModernKeePass.Interfaces
{
public interface ISelectableModel
{
bool IsSelected { get; set; }
}
}

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -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,20 +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\WelcomePage.xaml.cs">
<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">
@@ -147,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" />
@@ -186,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>
@@ -198,23 +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\WelcomePage.xaml">
<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>
@@ -223,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>
@@ -234,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">
@@ -298,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)' &lt; '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">

View File

@@ -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.3.0.17" />
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.5.0.25" />
<Properties>
<DisplayName>ModernKeePass</DisplayName>
<PublisherDisplayName>wismna</PublisherDisplayName>

View 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
}
}

View File

@@ -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="&#xE052;"
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="&#xE15E;"
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,56 +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 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}}" />
<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.Row="0">
<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}"
<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"/>
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<TextBox
Grid.Column="1"
x:Name="TitleTextBox"
Text="{Binding Title, Mode=TwoWay}"
Style="{StaticResource HeaderTextBoxStyle}"
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"
PlaceholderText="New entry name..."/>
<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>

View File

@@ -64,10 +64,6 @@ namespace ModernKeePass.Pages
navigationHelper.OnNavigatedTo(e);
if (!(e.Parameter is EntryVm)) return;
DataContext = (EntryVm)e.Parameter;
if (Model.IsEditMode)
Task.Factory.StartNew(
() => Dispatcher.RunAsync(CoreDispatcherPriority.Low,
() => TitleTextBox.Focus(FocusState.Programmatic)));
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
@@ -77,28 +73,9 @@ 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)

View File

@@ -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
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,7 +188,8 @@
</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 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" />-->
@@ -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,42 +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"
PlaceholderText="New group name..."/>
<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>

View File

@@ -8,6 +8,7 @@ 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
@@ -62,12 +63,10 @@ namespace ModernKeePass.Pages
{
NavigationHelper.OnNavigatedTo(e);
if (!(e.Parameter is GroupVm)) return;
if (e.Parameter is PasswordEventArgs)
DataContext = ((PasswordEventArgs) e.Parameter).RootGroup;
else if (e.Parameter is GroupVm)
DataContext = (GroupVm) e.Parameter;
if (Model.IsEditMode)
Task.Factory.StartNew(
() => Dispatcher.RunAsync(CoreDispatcherPriority.Low,
() => TitleTextBox.Focus(FocusState.Programmatic)));
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
@@ -114,27 +113,9 @@ namespace ModernKeePass.Pages
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 =>
{
Model.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)
@@ -149,10 +130,10 @@ namespace ModernKeePass.Pages
private void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx://Assets/Logo.scale-80.png"));
var results = Model.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);
}
}
@@ -163,6 +144,5 @@ namespace ModernKeePass.Pages
}
#endregion
}
}

View 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>

View File

@@ -1,22 +1,30 @@
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Pages;
using ModernKeePass.ViewModels;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass
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 MainVm Model => (MainVm)DataContext;
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)
@@ -25,10 +33,5 @@ namespace ModernKeePass
DataContext = new MainVm(Frame, MenuFrame);
if (Model.SelectedItem == null) MenuFrame.Navigate(typeof(WelcomePage));
}
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Model.SelectedItem?.Destination.Navigate(Model.SelectedItem.PageType, Model.SelectedItem.Parameter);
}
}
}

View 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>

View 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();
}
}
}

View 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>

View File

@@ -18,7 +18,7 @@ namespace ModernKeePass.Pages
{
private Frame _mainFrame;
public OpenVm Model => (OpenVm)DataContext;
public NewVm Model => (NewVm)DataContext;
public NewDatabasePage()
{
@@ -44,10 +44,5 @@ namespace ModernKeePass.Pages
if (file == null) return;
Model.OpenFile(file);
}
private void PasswordUserControl_PasswordChecked(object sender, PasswordEventArgs e)
{
_mainFrame.Navigate(typeof(GroupDetailPage), e.RootGroup);
}
}
}

View File

@@ -6,6 +6,7 @@
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>
@@ -20,7 +21,17 @@
<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>
<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" />
<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>

View File

@@ -41,12 +41,9 @@ namespace ModernKeePass.Pages
picker.FileTypeFilter.Add(".kdbx");
// Application now has read/write access to the picked file
Model.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);
}
}
}

View 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>

View File

@@ -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
@@ -29,11 +28,6 @@ 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)
{
var app = (App)Application.Current;

View File

@@ -7,13 +7,12 @@
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Style="{StaticResource HeaderTextBlockStyle}" Margin="0,-20,0,0">Welcome</TextBlock>
<StackPanel Orientation="Horizontal" Margin="0,20,0,10">
<SymbolIcon Symbol="Back" Margin="-30,7,40,0" />
<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">
<SymbolIcon Symbol="Back" Margin="-30,7,40,0" />
<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>

View File

@@ -1,24 +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:local="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 Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">Create a new password database to the location of your chosing.</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{Binding Name}" Height="auto" Width="auto" FontSize="16" Margin="10,7,0,6" />
<local:OpenDatabaseUserControl HorizontalAlignment="Left" CreateNew="True" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}" ValidationChecked="PasswordUserControl_PasswordChecked" />
</StackPanel>
</Page>

View File

@@ -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>

View 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>

View 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
}
}

View File

@@ -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.3.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyVersion("1.5.0.0")]
[assembly: AssemblyFileVersion("1.5.0.0")]
[assembly: ComVisible(false)]

View File

@@ -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>

View 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>

View 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>

View File

@@ -1,7 +1,7 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace ModernKeePass.Controls
namespace ModernKeePass.TemplateSelectors
{
public class FirstItemDataTemplateSelector: DataTemplateSelector
{

View 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}";
}
}
}
}

View File

@@ -1,13 +1,17 @@
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 : INotifyPropertyChanged
public class EntryVm : INotifyPropertyChanged, IPwEntity
{
public GroupVm ParentGroup { get; }
public PwEntry Entry { get; }
@@ -15,8 +19,21 @@ namespace ModernKeePass.ViewModels
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
{
@@ -40,6 +57,7 @@ namespace ModernKeePass.ViewModels
{
SetEntryValue(PwDefs.PasswordField, value);
NotifyPropertyChanged("Password");
NotifyPropertyChanged("PasswordComplexityIndicator");
}
}
public string Url
@@ -58,11 +76,23 @@ 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; }
@@ -82,11 +112,21 @@ namespace ModernKeePass.ViewModels
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));
@@ -99,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)
@@ -113,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();
}
}
}

View File

@@ -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;
}
@@ -48,12 +52,6 @@ namespace ModernKeePass.ViewModels
}
}
public bool IsLeftPaneOpen
{
get { return _isLeftPaneOpen; }
set { SetProperty(ref _isLeftPaneOpen, value); }
}
public bool IsEditMode
{
get { return _isEditMode; }
@@ -70,7 +68,7 @@ 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 ());
@@ -94,16 +92,26 @@ namespace ModernKeePass.ViewModels
return newEntry;
}
public void RemoveGroup()
public void MarkForDelete()
{
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
var app = (App)Application.Current;
app.PendingDeleteEntities.Add(Id, this);
ParentGroup.Groups.Remove(this);
}
public void RemoveEntry(EntryVm entry)
public void CommitDelete()
{
_pwGroup.Entries.Remove(entry.Entry);
Entries.Remove(entry);
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
}
public void UndoDelete()
{
ParentGroup.Groups.Add(this);
}
public void Save()
{
var app = (App)Application.Current;
app.Database.Save();
}
}
}

View File

@@ -5,7 +5,7 @@ using ModernKeePass.Interfaces;
namespace ModernKeePass.ViewModels
{
public class MainMenuItemVm: NotifyPropertyChangedBase, IIsEnabled
public class MainMenuItemVm: NotifyPropertyChangedBase, IIsEnabled, ISelectableModel
{
private bool _isSelected;

View File

@@ -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)
{
@@ -66,6 +71,10 @@ namespace ModernKeePass.ViewModels
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, 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

View 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");
}
}
}
}

View File

@@ -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));
}

View File

@@ -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)
{

View 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" />

View File

@@ -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 ()]
[TestFixture()]
public class StandardAesEngineTests
{
[Test ()]
public void TestEncryptStream ()
{
// 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] {
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;
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));
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));
var outBytes = new BinaryReaderEx(outStream, Encoding.UTF8, string.Empty).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;
// Possible Mono Bug? This only works with size >= 48
var inStream = new MemoryStream (new byte[48]);
inStream.Write (pbReferenceCT, 0, pbReferenceCT.Length);
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 BinaryReader (outStream).ReadBytes (16);
Assert.That(outBytes, Is.EqualTo (pbTestData));
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));
}
[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));
}
}
}

View File

@@ -1,18 +1,13 @@
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];
@@ -25,7 +20,11 @@ namespace ModernKeePassLib.Test.Shared.Keys
const ulong rounds = 1;
var composite = new CompositeKey ();
var key = composite.GenerateKey32 (originalKey, rounds);
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));

View File

@@ -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>

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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;

View 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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View File

@@ -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;
}
}

View 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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
private void Dispose(bool bDisposing)
{
// Clear sensitive data
Array.Clear(m_state, 0, m_state.Length);
Array.Clear(m_x, 0, m_x.Length);
base.Dispose(bDisposing);
}
private void NextOutput()
protected override void NextBlock(byte[] pBlock)
{
uint[] x = m_x; // Local alias for working buffer
if(pBlock == null) throw new ArgumentNullException("pBlock");
if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
// 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];
}
}
}

View File

@@ -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>
/// 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);
}
}
}

View File

@@ -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);
#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);
#endif
}
ms.Dispose();
{
m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
}
private static byte[] GetSystemData(Random rWeak)
MemUtil.ZeroByteArray(pbCmp);
}
}
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,17 +171,13 @@ 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);
@@ -187,76 +186,126 @@ namespace ModernKeePassLib.Cryptography
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,38 +314,31 @@ 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>
@@ -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);
}
}
}

View File

@@ -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,9 +250,11 @@ namespace ModernKeePassLib.Cryptography
int nStart = Environment.TickCount;
for(int i = 0; i < nRounds; ++i)
{
CryptoRandomStream c = new CryptoRandomStream(cra, pbKey);
using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey))
{
c.GetRandomBytes((uint)nDataSize);
}
}
int nEnd = Environment.TickCount;
return (nEnd - nStart);

View 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
}
}

View 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;
}
}
}
}

View 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();
}
}
}

View 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();
}
}
}

View 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));
}
}
}

View 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();
}*/
}
}

View File

@@ -0,0 +1,12 @@
using Org.BouncyCastle.Crypto.Digests;
namespace ModernKeePassLib.Cryptography.Hash
{
public class SHA256Managed : DigestManaged
{
public SHA256Managed()
{
Hash = new Sha256Digest();
}
}
}

View File

@@ -0,0 +1,12 @@
using Org.BouncyCastle.Crypto.Digests;
namespace ModernKeePassLib.Cryptography.Hash
{
public class SHA512Managed: DigestManaged
{
public SHA512Managed()
{
Hash = new Sha512Digest();
}
}
}

View File

@@ -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,30 +19,29 @@
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;
#else
private HashAlgorithm m_hash;
@@ -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,44 +102,34 @@ 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
}
public override void Flush()
{
m_sBaseStream.Flush();
}
#if ModernKeePassLib || KeePassRT
protected override void Dispose(bool disposing)
{
if(!disposing) return;
#else
public override void Close()
if(disposing)
{
#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_hash.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
m_pbFinalHash = m_hash.Hash;
#endif
}
@@ -156,6 +141,14 @@ namespace ModernKeePassLib.Cryptography
m_sBaseStream.Dispose();
}
base.Dispose(disposing);
}
public override void Flush()
{
m_sBaseStream.Flush();
}
public override long Seek(long lOffset, SeekOrigin soOrigin)
{
throw new NotSupportedException();
@@ -185,9 +178,11 @@ 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));
@@ -205,10 +200,13 @@ 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));
#endif

View File

@@ -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,20 +44,10 @@ 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);
if((iTruncationOffset >= 0) && (iTruncationOffset < (pbHash.Length - 4)))

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,96 @@
/*
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.KeyDerivation
{
public static class KdfPool
{
private static List<KdfEngine> g_l = new List<KdfEngine>();
public static IEnumerable<KdfEngine> Engines
{
get
{
EnsureInitialized();
return g_l;
}
}
private static void EnsureInitialized()
{
if(g_l.Count > 0) return;
g_l.Add(new AesKdf());
g_l.Add(new Argon2Kdf());
}
internal static KdfParameters GetDefaultParameters()
{
EnsureInitialized();
return g_l[0].GetDefaultParameters();
}
public static KdfEngine Get(PwUuid pu)
{
if(pu == null) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(KdfEngine kdf in g_l)
{
if(pu.Equals(kdf.Uuid)) return kdf;
}
return null;
}
public static KdfEngine Get(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(KdfEngine kdf in g_l)
{
if(strName.Equals(kdf.Name, StrUtil.CaseIgnoreCmp)) return kdf;
}
return null;
}
public static void Add(KdfEngine kdf)
{
if(kdf == null) { Debug.Assert(false); return; }
EnsureInitialized();
if(Get(kdf.Uuid) != null) { Debug.Assert(false); return; }
if(Get(kdf.Name) != null) { Debug.Assert(false); return; }
g_l.Add(kdf);
}
}
}

View File

@@ -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
@@ -47,7 +47,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
if(ch == char.MinValue)
{
Array.Clear(vGenerated, 0, vGenerated.Length);
MemUtil.ZeroArray<char>(vGenerated);
return PwgError.TooFewCharacters;
}
@@ -57,7 +57,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vGenerated);
psOut = new ProtectedString(true, pbUtf8);
MemUtil.ZeroByteArray(pbUtf8);
Array.Clear(vGenerated, 0, vGenerated.Length);
MemUtil.ZeroArray<char>(vGenerated);
return PwgError.Success;
}

View File

@@ -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

View File

@@ -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

Some files were not shown because too many files have changed in this diff Show More