towards sdk-style projects. As of now, all code/resources etc. is duplicated for new projects in new folders. this will be moved back to the original locations (and then squashed). App currently does not build.

This commit is contained in:
Philipp Crocoll
2024-11-05 10:58:57 +01:00
parent dad9b0e53f
commit 43f6064faa
1994 changed files with 261699 additions and 1467 deletions

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<None Remove="Transforms\EnumFields.xml" />
<None Remove="Transforms\EnumMethods.xml" />
<None Remove="Transforms\Metadata.xml" />
</ItemGroup>
<ItemGroup>
<AndroidLibrary Include="..\java\android-filechooser-AS\app\build\outputs\aar\android-filechooser-release.aar" Link="android-filechooser-release.aar" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
<enum-field-mappings>
<!--
This example converts the constants Fragment_id, Fragment_name,
and Fragment_tag from android.support.v4.app.FragmentActivity.FragmentTag
to an enum called Android.Support.V4.App.FragmentTagType with values
Id, Name, and Tag.
<mapping jni-class="android/support/v4/app/FragmentActivity$FragmentTag" clr-enum-type="Android.Support.V4.App.FragmentTagType">
<field jni-name="Fragment_name" clr-name="Name" value="0" />
<field jni-name="Fragment_id" clr-name="Id" value="1" />
<field jni-name="Fragment_tag" clr-name="Tag" value="2" />
</mapping>
-->
</enum-field-mappings>

View File

@@ -0,0 +1,13 @@
<enum-method-mappings>
<!--
This example changes the Java method:
android.support.v4.app.Fragment.SavedState.writeToParcel (int flags)
to be:
android.support.v4.app.Fragment.SavedState.writeToParcel (Android.OS.ParcelableWriteFlags flags)
when bound in C#.
<mapping jni-class="android/support/v4/app/Fragment.SavedState">
<method jni-name="writeToParcel" parameter="flags" clr-enum-type="Android.OS.ParcelableWriteFlags" />
</mapping>
-->
</enum-method-mappings>

View File

@@ -0,0 +1,22 @@
<metadata>
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser.providers.localfile']/class[@name='FileObserverEx']" />
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser']/class[@name='FragmentFiles']" />
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser.prefs']" />
<!--
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser']/class[@name='FragmentFiles']" />
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser.utils.ui.bookmark']/class[@name='BookmarkFragment']" />
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser.utils.ui.history']/class[@name='HistoryFragment']" />
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser.prefs']/class[@name='DisplayPrefs']" />
This sample removes the class: android.support.v4.content.AsyncTaskLoader.LoadTask:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='AsyncTaskLoader.LoadTask']" />
This sample removes the method: android.support.v4.content.CursorLoader.loadInBackground:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='CursorLoader']/method[@name='loadInBackground']" />
-->
</metadata>

View File

@@ -0,0 +1,48 @@
Additions allow you to add arbitrary C# to the generated classes
before they are compiled. This can be helpful for providing convenience
methods or adding pure C# classes.
== Adding Methods to Generated Classes ==
Let's say the library being bound has a Rectangle class with a constructor
that takes an x and y position, and a width and length size. It will look like
this:
public partial class Rectangle
{
public Rectangle (int x, int y, int width, int height)
{
// JNI bindings
}
}
Imagine we want to add a constructor to this class that takes a Point and
Size structure instead of 4 ints. We can add a new file called Rectangle.cs
with a partial class containing our new method:
public partial class Rectangle
{
public Rectangle (Point location, Size size) :
this (location.X, location.Y, size.Width, size.Height)
{
}
}
At compile time, the additions class will be added to the generated class
and the final assembly will a Rectangle class with both constructors.
== Adding C# Classes ==
Another thing that can be done is adding fully C# managed classes to the
generated library. In the above example, let's assume that there isn't a
Point class available in Java or our library. The one we create doesn't need
to interact with Java, so we'll create it like a normal class in C#.
By adding a Point.cs file with this class, it will end up in the binding library:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<AndroidLibrary Include="..\java\JavaFileStorage\app\build\outputs\aar\JavaFileStorage-debug.aar" Link="JavaFileStorage-debug.aar" />
</ItemGroup>
<ItemGroup>
<AndroidLibrary Bind="False" Update="dropbox-core-sdk-5.4.6.jar" />
<AndroidLibrary Bind="False" Update="gdrive\commons-logging-1.1.1.jar" />
<AndroidLibrary Bind="False" Update="gdrive\google-api-client-1.30.5.jar" />
<AndroidLibrary Bind="False" Update="gdrive\google-api-client-android-1.30.5.jar" />
<AndroidLibrary Bind="False" Update="gdrive\google-api-services-drive-v2-rev102-1.16.0-rc.jar" />
<AndroidLibrary Bind="False" Update="gdrive\google-http-client-1.32.1.jar" />
<AndroidLibrary Bind="False" Update="gdrive\google-http-client-android-1.32.1.jar" />
<AndroidLibrary Bind="False" Update="gdrive\google-http-client-gson-1.16.0-rc.jar" />
<AndroidLibrary Bind="False" Update="gdrive\google-http-client-jackson-1.16.0-rc.jar" />
<AndroidLibrary Bind="False" Update="gdrive\google-http-client-jackson2-1.32.1.jar" />
<AndroidLibrary Bind="False" Update="gdrive\google-oauth-client-1.30.4.jar" />
<AndroidLibrary Bind="False" Update="gdrive\grpc-context-1.22.1.jar" />
<AndroidLibrary Bind="False" Update="gdrive\httpclient-4.0.3.jar" />
<AndroidLibrary Bind="False" Update="gdrive\httpcore-4.0.1.jar" />
<AndroidLibrary Bind="False" Update="gdrive\httpmime-4.0.3.jar" />
<AndroidLibrary Bind="False" Update="gdrive\json_simple-1.1.jar" />
<AndroidLibrary Bind="False" Update="gdrive\jsr305-3.0.2.jar" />
<AndroidLibrary Bind="False" Update="gdrive\opencensus-api-0.24.0.jar" />
<AndroidLibrary Bind="False" Update="gdrive\opencensus-contrib-http-util-0.24.0.jar" />
<AndroidLibrary Bind="False" Update="gson-2.8.6.jar" />
<AndroidLibrary Bind="False" Update="jackson-core-2.13.5.jar" />
<AndroidLibrary Bind="False" Update="okhttp-4.12.0.jar" />
<AndroidLibrary Bind="False" Update="okhttp-digest-3.1.0.jar" />
<AndroidLibrary Bind="False" Update="okio-3.6.0.jar" />
<AndroidLibrary Bind="False" Update="okio-jvm-3.6.0.jar" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
<enum-field-mappings>
<!--
This example converts the constants Fragment_id, Fragment_name,
and Fragment_tag from android.support.v4.app.FragmentActivity.FragmentTag
to an enum called Android.Support.V4.App.FragmentTagType with values
Id, Name, and Tag.
<mapping jni-class="android/support/v4/app/FragmentActivity$FragmentTag" clr-enum-type="Android.Support.V4.App.FragmentTagType">
<field jni-name="Fragment_name" clr-name="Name" value="0" />
<field jni-name="Fragment_id" clr-name="Id" value="1" />
<field jni-name="Fragment_tag" clr-name="Tag" value="2" />
</mapping>
-->
</enum-field-mappings>

View File

@@ -0,0 +1,13 @@
<enum-method-mappings>
<!--
This example changes the Java method:
android.support.v4.app.Fragment.SavedState.writeToParcel (int flags)
to be:
android.support.v4.app.Fragment.SavedState.writeToParcel (Android.OS.ParcelableWriteFlags flags)
when bound in C#.
<mapping jni-class="android/support/v4/app/Fragment.SavedState">
<method jni-name="writeToParcel" parameter="flags" clr-enum-type="Android.OS.ParcelableWriteFlags" />
</mapping>
-->
</enum-method-mappings>

View File

@@ -0,0 +1,29 @@
<metadata>
<!--
This sample removes the class: android.support.v4.content.AsyncTaskLoader.LoadTask:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='AsyncTaskLoader.LoadTask']" />
This sample removes the method: android.support.v4.content.CursorLoader.loadInBackground:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='CursorLoader']/method[@name='loadInBackground']" />
-->
<remove-node path="/api/package[@name='com.jcraft.jsch']" />
<remove-node path="/api/package[@name='com.jcraft.jsch.jce']" />
<remove-node path="/api/package[@name='com.jcraft.jsch.jcraft']" />
<remove-node path="/api/package[@name='com.jcraft.jzlib']" />
<remove-node path="/api/package[@name='com.pcloud.sdk']" />
<remove-node path="/api/package[@name='io.opencensus.stats']" />
<remove-node path="/api/package[@name='io.opencensus']" />
<remove-node path="/api/package[@name='com.dropbox.core']" />
<remove-node path="/api/package[@name='com.dropbox.core']" />
<remove-node path="/api/package[@name='com.dropbox.core.util']" />
<remove-node path="/api/package[@name='com.dropbox.core.http']" />
<remove-node path="/api/package[@name='com.dropbox.core.v2.sharing']" />
<remove-node path="/api/package[@name='com.dropbox.core.v2.team']" />
<remove-node path="/api/package[@name='keepass2android.javafilestorage.webdav']" />
<remove-node path="/api/package[@name='keepass2android.javafilestorage.onedrive']" />
</metadata>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<None Remove="Transforms\EnumFields.xml" />
<None Remove="Transforms\EnumMethods.xml" />
<None Remove="Transforms\Metadata.xml" />
</ItemGroup>
<ItemGroup>
<AndroidLibrary Include="..\java\KP2AKdbLibrary\app\build\outputs\aar\app-debug.aar" Link="app-debug.aar" />
</ItemGroup>
<ItemGroup>
<TransformFile Update="Transforms\Metadata.xml">
<SubType>Designer</SubType>
</TransformFile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
<enum-field-mappings>
<!--
This example converts the constants Fragment_id, Fragment_name,
and Fragment_tag from android.support.v4.app.FragmentActivity.FragmentTag
to an enum called Android.Support.V4.App.FragmentTagType with values
Id, Name, and Tag.
<mapping jni-class="android/support/v4/app/FragmentActivity$FragmentTag" clr-enum-type="Android.Support.V4.App.FragmentTagType">
<field jni-name="Fragment_name" clr-name="Name" value="0" />
<field jni-name="Fragment_id" clr-name="Id" value="1" />
<field jni-name="Fragment_tag" clr-name="Tag" value="2" />
</mapping>
-->
</enum-field-mappings>

View File

@@ -0,0 +1,13 @@
<enum-method-mappings>
<!--
This example changes the Java method:
android.support.v4.app.Fragment.SavedState.writeToParcel (int flags)
to be:
android.support.v4.app.Fragment.SavedState.writeToParcel (Android.OS.ParcelableWriteFlags flags)
when bound in C#.
<mapping jni-class="android/support/v4/app/Fragment.SavedState">
<method jni-name="writeToParcel" parameter="flags" clr-enum-type="Android.OS.ParcelableWriteFlags" />
</mapping>
-->
</enum-method-mappings>

View File

@@ -0,0 +1,32 @@
<metadata>
<!--
This sample removes the class: android.support.v4.content.AsyncTaskLoader.LoadTask:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='AsyncTaskLoader.LoadTask']" />
This sample removes the method: android.support.v4.content.CursorLoader.loadInBackground:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='CursorLoader']/method[@name='loadInBackground']" />
-->
<remove-node path="/api/package[@name='org.apache.commons.collections']" />
<remove-node path="/api/package[@name='org.apache.commons.collections.iterators']" />
<remove-node path="/api/package[@name='org.apache.commons.collections.keyvalue']" />
<remove-node path="/api/package[@name='org.apache.commons.collections.map']" />
<remove-node path="/api/package[@name='org.bouncycastle.asn1']" />
<remove-node path="/api/package[@name='org.bouncycastle.asn1.oiw']" />
<remove-node path="/api/package[@name='org.bouncycastle.asn1.pkcs']" />
<remove-node path="/api/package[@name='org.bouncycastle.asn1.util']" />
<remove-node path="/api/package[@name='org.bouncycastle.crypto']" />
<remove-node path="/api/package[@name='org.bouncycastle.crypto.digests']" />
<remove-node path="/api/package[@name='org.bouncycastle.crypto.engines']" />
<remove-node path="/api/package[@name='org.bouncycastle.crypto.generators']" />
<remove-node path="/api/package[@name='org.bouncycastle.crypto.macs']" />
<remove-node path="/api/package[@name='org.bouncycastle.crypto.modes']" />
<remove-node path="/api/package[@name='org.bouncycastle.crypto.paddings']" />
<remove-node path="/api/package[@name='org.bouncycastle.crypto.params']" />
<remove-node path="/api/package[@name='org.bouncycastle.crypto.util']" />
<remove-node path="/api/package[@name='org.bouncycastle.jce.interfaces']" />
<remove-node path="/api/package[@name='org.bouncycastle.jce.provider']" />
<remove-node path="/api/package[@name='org.bouncycastle.util']" />
<remove-node path="/api/package[@name='org.bouncycastle.util.encoders']" />
<remove-node path="/api/package[@name='org.bouncycastle.util.io']" />
</metadata>

View File

@@ -29,6 +29,24 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aAutofillParser", "Kp2aA
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aAutofillParserTest", "Kp2aAutofillParserTest\Kp2aAutofillParserTest.csproj", "{3D1560FF-86BB-4CB4-8367-80BA13B81C38}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aAutofillParserTest", "Kp2aAutofillParserTest\Kp2aAutofillParserTest.csproj", "{3D1560FF-86BB-4CB4-8367-80BA13B81C38}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZlibAndroidSdkStyle", "ZlibAndroidSdkStyle\ZlibAndroidSdkStyle.csproj", "{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TwofishCipherSdkStyle", "TwofishCipherSdkStyle\TwofishCipherSdkStyle.csproj", "{69D491AA-3A31-4467-8216-3641A4F157BE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeePassLib2AndroidSdkStyle", "KeePassLib2AndroidSdkStyle\KeePassLib2AndroidSdkStyle.csproj", "{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KP2AKdbLibraryBindingSdkStyle", "KP2AKdbLibraryBindingSdkStyle\KP2AKdbLibraryBindingSdkStyle.csproj", "{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AndroidFileChooserBindingSdkStyle", "AndroidFileChooserBindingSdkStyle\AndroidFileChooserBindingSdkStyle.csproj", "{C3A88E81-0809-49A4-A4EC-DF71A6200F41}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JavaFileStorageBindingsStkStyle", "JavaFileStorageBindingsStkStyle\JavaFileStorageBindingsStkStyle.csproj", "{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aBusinessLogicSdkStyle", "Kp2aBusinessLogicSdkStyle\Kp2aBusinessLogicSdkStyle.csproj", "{862D7A27-4211-4258-A4B0-741B4C0A73CF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aAutofillParserSdkStyle", "Kp2aAutofillParserSdkStyle\Kp2aAutofillParserSdkStyle.csproj", "{2D5E3AEF-6EB2-4737-9ACB-921A22928682}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "keepass2android-appSdkStyle", "keepass2android-appSdkStyle\keepass2android-appSdkStyle.csproj", "{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -335,6 +353,234 @@ Global
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU {3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU {3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.Build.0 = Release|Any CPU {3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Debug|Win32.ActiveCfg = Debug|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Debug|Win32.Build.0 = Debug|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Debug|x64.ActiveCfg = Debug|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Debug|x64.Build.0 = Debug|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Release|Any CPU.Build.0 = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Release|Win32.ActiveCfg = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Release|Win32.Build.0 = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Release|x64.ActiveCfg = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.Release|x64.Build.0 = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{1DF9DA08-D2FE-4227-BD53-761CD3F6CA42}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Debug|Win32.ActiveCfg = Debug|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Debug|Win32.Build.0 = Debug|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Debug|x64.ActiveCfg = Debug|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Debug|x64.Build.0 = Debug|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Release|Any CPU.Build.0 = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Release|Win32.ActiveCfg = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Release|Win32.Build.0 = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Release|x64.ActiveCfg = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.Release|x64.Build.0 = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{69D491AA-3A31-4467-8216-3641A4F157BE}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Debug|Win32.ActiveCfg = Debug|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Debug|Win32.Build.0 = Debug|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Debug|x64.ActiveCfg = Debug|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Debug|x64.Build.0 = Debug|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Release|Any CPU.Build.0 = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Release|Win32.ActiveCfg = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Release|Win32.Build.0 = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Release|x64.ActiveCfg = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.Release|x64.Build.0 = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{6E1DCFE1-D86D-480F-BE02-BBE8FD4601F0}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Debug|Win32.ActiveCfg = Debug|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Debug|Win32.Build.0 = Debug|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Debug|x64.ActiveCfg = Debug|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Debug|x64.Build.0 = Debug|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Release|Any CPU.Build.0 = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Release|Win32.ActiveCfg = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Release|Win32.Build.0 = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Release|x64.ActiveCfg = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.Release|x64.Build.0 = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{BF542E42-E7A9-4C71-AA3B-DAC0F959F0D5}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Debug|Win32.ActiveCfg = Debug|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Debug|Win32.Build.0 = Debug|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Debug|x64.ActiveCfg = Debug|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Debug|x64.Build.0 = Debug|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Release|Any CPU.Build.0 = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Release|Win32.ActiveCfg = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Release|Win32.Build.0 = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Release|x64.ActiveCfg = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.Release|x64.Build.0 = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{C3A88E81-0809-49A4-A4EC-DF71A6200F41}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Debug|Win32.ActiveCfg = Debug|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Debug|Win32.Build.0 = Debug|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Debug|x64.ActiveCfg = Debug|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Debug|x64.Build.0 = Debug|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Release|Any CPU.Build.0 = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Release|Win32.ActiveCfg = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Release|Win32.Build.0 = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Release|x64.ActiveCfg = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.Release|x64.Build.0 = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{4F2E6B45-2C9F-4DF6-A9DC-9F81DC8681BC}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Debug|Win32.ActiveCfg = Debug|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Debug|Win32.Build.0 = Debug|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Debug|x64.ActiveCfg = Debug|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Debug|x64.Build.0 = Debug|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Release|Any CPU.Build.0 = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Release|Win32.ActiveCfg = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Release|Win32.Build.0 = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Release|x64.ActiveCfg = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.Release|x64.Build.0 = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{862D7A27-4211-4258-A4B0-741B4C0A73CF}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Debug|Win32.ActiveCfg = Debug|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Debug|Win32.Build.0 = Debug|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Debug|x64.ActiveCfg = Debug|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Debug|x64.Build.0 = Debug|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Release|Any CPU.Build.0 = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Release|Win32.ActiveCfg = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Release|Win32.Build.0 = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Release|x64.ActiveCfg = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.Release|x64.Build.0 = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{2D5E3AEF-6EB2-4737-9ACB-921A22928682}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|Win32.ActiveCfg = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|Win32.Build.0 = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|Win32.Deploy.0 = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|x64.ActiveCfg = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|x64.Build.0 = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Debug|x64.Deploy.0 = Debug|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|Any CPU.Build.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|Any CPU.Deploy.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|Win32.ActiveCfg = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|Win32.Build.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|Win32.Deploy.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|x64.ActiveCfg = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|x64.Build.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.Release|x64.Deploy.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|Win32.Deploy.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{0CCDA3EB-8A24-4A42-BC91-A4DD254504E7}.ReleaseNoNet|x64.Deploy.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -32,9 +32,10 @@
<Optimize>True</Optimize> <Optimize>True</Optimize>
<OutputPath>bin\Release</OutputPath> <OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>5</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime> <AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<ConsolePause>False</ConsolePause> <ConsolePause>False</ConsolePause>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
@@ -149,7 +150,6 @@
<Compile Include="Serialization\KdbxFile.Write.cs" /> <Compile Include="Serialization\KdbxFile.Write.cs" />
<Compile Include="Serialization\IOConnectionInfo.cs" /> <Compile Include="Serialization\IOConnectionInfo.cs" />
<Compile Include="Serialization\OldFormatException.cs" /> <Compile Include="Serialization\OldFormatException.cs" />
<Compile Include="Serialization\ProtoBuf\KdbpFile.cs" />
<Compile Include="Translation\KPStringTable.cs" /> <Compile Include="Translation\KPStringTable.cs" />
<Compile Include="Translation\KPStringTableItem.cs" /> <Compile Include="Translation\KPStringTableItem.cs" />
<Compile Include="Translation\KPTranslation.cs" /> <Compile Include="Translation\KPTranslation.cs" />
@@ -179,5 +179,8 @@
<Name>KP2AKdbLibraryBinding</Name> <Name>KP2AKdbLibraryBinding</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Serialization\ProtoBuf\" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
</Project> </Project>

File diff suppressed because it is too large Load Diff

View File

@@ -687,25 +687,6 @@ namespace KeePassLib.Utility
return false; return false;
} }
public static string GetTempPath()
{
string strDir;
if (NativeLib.IsUnix())
strDir = NativeMethods.GetUserRuntimeDir();
#if KeePassUAP
else strDir = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
#else
else strDir = Path.GetTempPath();
#endif
try
{
if (!Directory.Exists(strDir)) Directory.CreateDirectory(strDir);
}
catch (Exception) { Debug.Assert(false); }
return strDir;
}
#if !KeePassLibSD #if !KeePassLibSD
// Structurally mostly equivalent to UrlUtil.GetFileInfos // Structurally mostly equivalent to UrlUtil.GetFileInfos

View File

@@ -0,0 +1,244 @@
/*
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 KeePassLib.Interfaces;
namespace KeePassLib.Collections
{
[Flags]
public enum AutoTypeObfuscationOptions
{
None = 0,
UseClipboard = 1
}
public sealed class AutoTypeAssociation : IEquatable<AutoTypeAssociation>,
IDeepCloneable<AutoTypeAssociation>
{
private string m_strWindow = string.Empty;
public string WindowName
{
get { return m_strWindow; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_strWindow = value;
}
}
private string m_strSequence = string.Empty;
public string Sequence
{
get { return m_strSequence; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_strSequence = value;
}
}
public AutoTypeAssociation() { }
public AutoTypeAssociation(string strWindow, string strSeq)
{
if(strWindow == null) throw new ArgumentNullException("strWindow");
if(strSeq == null) throw new ArgumentNullException("strSeq");
m_strWindow = strWindow;
m_strSequence = strSeq;
}
public bool Equals(AutoTypeAssociation other)
{
if(other == null) return false;
if(m_strWindow != other.m_strWindow) return false;
if(m_strSequence != other.m_strSequence) return false;
return true;
}
public AutoTypeAssociation CloneDeep()
{
return (AutoTypeAssociation)this.MemberwiseClone();
}
}
/// <summary>
/// A list of auto-type associations.
/// </summary>
public sealed class AutoTypeConfig : IEquatable<AutoTypeConfig>,
IDeepCloneable<AutoTypeConfig>
{
private bool m_bEnabled = true;
private AutoTypeObfuscationOptions m_atooObfuscation =
AutoTypeObfuscationOptions.None;
private string m_strDefaultSequence = string.Empty;
private List<AutoTypeAssociation> m_lWindowAssocs =
new List<AutoTypeAssociation>();
/// <summary>
/// Specify whether auto-type is enabled or not.
/// </summary>
public bool Enabled
{
get { return m_bEnabled; }
set { m_bEnabled = value; }
}
/// <summary>
/// Specify whether the typing should be obfuscated.
/// </summary>
public AutoTypeObfuscationOptions ObfuscationOptions
{
get { return m_atooObfuscation; }
set { m_atooObfuscation = value; }
}
/// <summary>
/// The default keystroke sequence that is auto-typed if
/// no matching window is found in the <c>Associations</c>
/// container.
/// </summary>
public string DefaultSequence
{
get { return m_strDefaultSequence; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_strDefaultSequence = value;
}
}
/// <summary>
/// Get all auto-type window/keystroke sequence pairs.
/// </summary>
public IEnumerable<AutoTypeAssociation> Associations
{
get { return m_lWindowAssocs; }
}
public int AssociationsCount
{
get { return m_lWindowAssocs.Count; }
}
/// <summary>
/// Construct a new auto-type associations list.
/// </summary>
public AutoTypeConfig()
{
}
/// <summary>
/// Remove all associations.
/// </summary>
public void Clear()
{
m_lWindowAssocs.Clear();
}
/// <summary>
/// Clone the auto-type associations list.
/// </summary>
/// <returns>New, cloned object.</returns>
public AutoTypeConfig CloneDeep()
{
AutoTypeConfig newCfg = new AutoTypeConfig();
newCfg.m_bEnabled = m_bEnabled;
newCfg.m_atooObfuscation = m_atooObfuscation;
newCfg.m_strDefaultSequence = m_strDefaultSequence;
foreach(AutoTypeAssociation a in m_lWindowAssocs)
newCfg.Add(a.CloneDeep());
return newCfg;
}
public bool Equals(AutoTypeConfig other)
{
if(other == null) { Debug.Assert(false); return false; }
if(m_bEnabled != other.m_bEnabled) return false;
if(m_atooObfuscation != other.m_atooObfuscation) return false;
if(m_strDefaultSequence != other.m_strDefaultSequence) return false;
if(m_lWindowAssocs.Count != other.m_lWindowAssocs.Count) return false;
for(int i = 0; i < m_lWindowAssocs.Count; ++i)
{
if(!m_lWindowAssocs[i].Equals(other.m_lWindowAssocs[i]))
return false;
}
return true;
}
public AutoTypeAssociation GetAt(int iIndex)
{
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
throw new ArgumentOutOfRangeException("iIndex");
return m_lWindowAssocs[iIndex];
}
public void Add(AutoTypeAssociation a)
{
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
m_lWindowAssocs.Add(a);
}
public void Insert(int iIndex, AutoTypeAssociation a)
{
if((iIndex < 0) || (iIndex > m_lWindowAssocs.Count))
throw new ArgumentOutOfRangeException("iIndex");
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
m_lWindowAssocs.Insert(iIndex, a);
}
public void RemoveAt(int iIndex)
{
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
throw new ArgumentOutOfRangeException("iIndex");
m_lWindowAssocs.RemoveAt(iIndex);
}
// public void Sort()
// {
// m_lWindowAssocs.Sort(AutoTypeConfig.AssocCompareFn);
// }
// private static int AssocCompareFn(AutoTypeAssociation x,
// AutoTypeAssociation y)
// {
// if(x == null) { Debug.Assert(false); return ((y == null) ? 0 : -1); }
// if(y == null) { Debug.Assert(false); return 1; }
// int cn = x.WindowName.CompareTo(y.WindowName);
// if(cn != 0) return cn;
// return x.Sequence.CompareTo(y.Sequence);
// }
}
}

View File

@@ -0,0 +1,178 @@
/*
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.Text;
using System.Diagnostics;
using KeePassLib.Interfaces;
using KeePassLib.Security;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace KeePassLib.Collections
{
/// <summary>
/// A list of <c>ProtectedBinary</c> objects (dictionary).
/// </summary>
public sealed class ProtectedBinaryDictionary :
IDeepCloneable<ProtectedBinaryDictionary>,
IEnumerable<KeyValuePair<string, ProtectedBinary>>
{
/*
private SortedDictionary<string, ProtectedBinary> m_vBinaries =
new SortedDictionary<string, ProtectedBinary>();
*/
private Dictionary<string, ProtectedBinary> m_vBinaries =
new Dictionary<string, ProtectedBinary>();
/// <summary>
/// Get the number of binaries in this entry.
/// </summary>
public uint UCount
{
get { return (uint)m_vBinaries.Count; }
}
/// <summary>
/// Construct a new list of protected binaries.
/// </summary>
public ProtectedBinaryDictionary()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vBinaries.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, ProtectedBinary>> GetEnumerator()
{
return m_vBinaries.GetEnumerator();
}
public void Clear()
{
m_vBinaries.Clear();
}
/// <summary>
/// Clone the current <c>ProtectedBinaryList</c> object, including all
/// stored protected strings.
/// </summary>
/// <returns>New <c>ProtectedBinaryList</c> object.</returns>
public ProtectedBinaryDictionary CloneDeep()
{
ProtectedBinaryDictionary plNew = new ProtectedBinaryDictionary();
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in m_vBinaries)
{
// ProtectedBinary objects are immutable
plNew.Set(kvpBin.Key, kvpBin.Value);
}
return plNew;
}
public bool EqualsDictionary(ProtectedBinaryDictionary dict)
{
if(dict == null) { Debug.Assert(false); return false; }
if(m_vBinaries.Count != dict.m_vBinaries.Count) return false;
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_vBinaries)
{
ProtectedBinary pb = dict.Get(kvp.Key);
if(pb == null) return false;
if(!pb.Equals(kvp.Value)) return false;
}
return true;
}
/// <summary>
/// Get one of the stored binaries.
/// </summary>
/// <param name="strName">Binary identifier.</param>
/// <returns>Protected binary. If the binary identified by
/// <paramref name="strName" /> cannot be found, the function
/// returns <c>null</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public ProtectedBinary Get(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedBinary pb;
if(m_vBinaries.TryGetValue(strName, out pb)) return pb;
return null;
}
/// <summary>
/// Set a binary object.
/// </summary>
/// <param name="strField">Identifier of the binary field to modify.</param>
/// <param name="pbNewValue">New value. This parameter must not be <c>null</c>.</param>
/// <exception cref="System.ArgumentNullException">Thrown if any of the input
/// parameters is <c>null</c>.</exception>
public void Set(string strField, ProtectedBinary pbNewValue)
{
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
Debug.Assert(pbNewValue != null); if(pbNewValue == null) throw new ArgumentNullException("pbNewValue");
m_vBinaries[strField] = pbNewValue;
}
/// <summary>
/// Remove a binary object.
/// </summary>
/// <param name="strField">Identifier of the binary field to remove.</param>
/// <returns>Returns <c>true</c> if the object has been successfully
/// removed, otherwise <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");
return m_vBinaries.Remove(strField);
}
public string KeysToString()
{
if(m_vBinaries.Count == 0) return string.Empty;
StringBuilder sb = new StringBuilder();
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_vBinaries)
{
if(sb.Length > 0) sb.Append(", ");
sb.Append(kvp.Key);
}
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,170 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 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 KeePassLib.Delegates;
using KeePassLib.Security;
namespace KeePassLib.Collections
{
internal sealed class ProtectedBinarySet : IEnumerable<KeyValuePair<int, ProtectedBinary>>
{
private Dictionary<int, ProtectedBinary> m_d =
new Dictionary<int, ProtectedBinary>();
private readonly bool m_bDedupAdd;
public ProtectedBinarySet(bool bDedupAdd)
{
m_bDedupAdd = bDedupAdd;
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_d.GetEnumerator();
}
public IEnumerator<KeyValuePair<int, ProtectedBinary>> GetEnumerator()
{
return m_d.GetEnumerator();
}
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; }
if (m_bDedupAdd && (Find(pb) >= 0)) return; // Exists already
m_d[GetFreeID()] = 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

@@ -0,0 +1,304 @@
/*
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 KeePassLib.Interfaces;
using KeePassLib.Security;
using KeePassLib.Utility;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace KeePassLib.Collections
{
/// <summary>
/// A list of <c>ProtectedString</c> objects (dictionary).
/// </summary>
public sealed class ProtectedStringDictionary :
IDeepCloneable<ProtectedStringDictionary>,
IEnumerable<KeyValuePair<string, ProtectedString>>
{
/*private SortedDictionary<string, ProtectedString> m_vStrings =
new SortedDictionary<string, ProtectedString>();*/
private Dictionary<string, ProtectedString> m_vStrings = new Dictionary<string, ProtectedString>();
/// <summary>
/// Get the number of strings in this entry.
/// </summary>
public uint UCount
{
get { return (uint)m_vStrings.Count; }
}
/// <summary>
/// Construct a new list of protected strings.
/// </summary>
public ProtectedStringDictionary()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vStrings.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, ProtectedString>> GetEnumerator()
{
return m_vStrings.GetEnumerator();
}
public void Clear()
{
m_vStrings.Clear();
}
/// <summary>
/// Clone the current <c>ProtectedStringList</c> object, including all
/// stored protected strings.
/// </summary>
/// <returns>New <c>ProtectedStringList</c> object.</returns>
public ProtectedStringDictionary CloneDeep()
{
ProtectedStringDictionary plNew = new ProtectedStringDictionary();
foreach(KeyValuePair<string, ProtectedString> kvpStr in m_vStrings)
{
// ProtectedString objects are immutable
plNew.Set(kvpStr.Key, kvpStr.Value);
}
return plNew;
}
[Obsolete]
public bool EqualsDictionary(ProtectedStringDictionary dict)
{
return EqualsDictionary(dict, PwCompareOptions.None, MemProtCmpMode.None);
}
[Obsolete]
public bool EqualsDictionary(ProtectedStringDictionary dict,
MemProtCmpMode mpCompare)
{
return EqualsDictionary(dict, PwCompareOptions.None, mpCompare);
}
public bool EqualsDictionary(ProtectedStringDictionary dict,
PwCompareOptions pwOpt, MemProtCmpMode mpCompare)
{
if(dict == null) { Debug.Assert(false); return false; }
bool bNeEqStd = ((pwOpt & PwCompareOptions.NullEmptyEquivStd) !=
PwCompareOptions.None);
if(!bNeEqStd)
{
if(m_vStrings.Count != dict.m_vStrings.Count) return false;
}
foreach(KeyValuePair<string, ProtectedString> kvp in m_vStrings)
{
bool bStdField = PwDefs.IsStandardField(kvp.Key);
ProtectedString ps = dict.Get(kvp.Key);
if(bNeEqStd && (ps == null) && bStdField)
ps = ProtectedString.Empty;
if(ps == null) return false;
if(mpCompare == MemProtCmpMode.Full)
{
if(ps.IsProtected != kvp.Value.IsProtected) return false;
}
else if(mpCompare == MemProtCmpMode.CustomOnly)
{
if(!bStdField && (ps.IsProtected != kvp.Value.IsProtected))
return false;
}
if(ps.ReadString() != kvp.Value.ReadString()) return false;
}
if(bNeEqStd)
{
foreach(KeyValuePair<string, ProtectedString> kvp in dict.m_vStrings)
{
ProtectedString ps = Get(kvp.Key);
if(ps != null) continue; // Compared previously
if(!PwDefs.IsStandardField(kvp.Key)) return false;
if(!kvp.Value.IsEmpty) return false;
}
}
return true;
}
/// <summary>
/// Get one of the protected strings.
/// </summary>
/// <param name="strName">String identifier.</param>
/// <returns>Protected string. If the string identified by
/// <paramref name="strName" /> cannot be found, the function
/// returns <c>null</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input parameter
/// is <c>null</c>.</exception>
public ProtectedString Get(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedString ps;
if(m_vStrings.TryGetValue(strName, out ps)) return ps;
return null;
}
/// <summary>
/// Get one of the protected strings. The return value is never <c>null</c>.
/// If the requested string cannot be found, an empty protected string
/// object is returned.
/// </summary>
/// <param name="strName">String identifier.</param>
/// <returns>Returns a protected string object. If the standard string
/// has not been set yet, the return value is an empty string (<c>""</c>).</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public ProtectedString GetSafe(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedString ps;
if(m_vStrings.TryGetValue(strName, out ps)) return ps;
return ProtectedString.Empty;
}
/// <summary>
/// Test if a named string exists.
/// </summary>
/// <param name="strName">Name of the string to try.</param>
/// <returns>Returns <c>true</c> if the string exists, otherwise <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if
/// <paramref name="strName" /> is <c>null</c>.</exception>
public bool Exists(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
return m_vStrings.ContainsKey(strName);
}
/// <summary>
/// Get one of the protected strings. If the string doesn't exist, the
/// return value is an empty string (<c>""</c>).
/// </summary>
/// <param name="strName">Name of the requested string.</param>
/// <returns>Requested string value or an empty string, if the named
/// string doesn't exist.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public string ReadSafe(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedString ps;
if(m_vStrings.TryGetValue(strName, out ps))
return ps.ReadString();
return string.Empty;
}
/// <summary>
/// Get one of the entry strings. If the string doesn't exist, the
/// return value is an empty string (<c>""</c>). If the string is
/// in-memory protected, the return value is <c>PwDefs.HiddenPassword</c>.
/// </summary>
/// <param name="strName">Name of the requested string.</param>
/// <returns>Returns the requested string in plain-text or
/// <c>PwDefs.HiddenPassword</c> if the string cannot be found.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public string ReadSafeEx(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedString ps;
if(m_vStrings.TryGetValue(strName, out ps))
{
if(ps.IsProtected) return PwDefs.HiddenPassword;
return ps.ReadString();
}
return string.Empty;
}
/// <summary>
/// Set a string.
/// </summary>
/// <param name="strField">Identifier of the string field to modify.</param>
/// <param name="psNewValue">New value. This parameter must not be <c>null</c>.</param>
/// <exception cref="System.ArgumentNullException">Thrown if one of the input
/// parameters is <c>null</c>.</exception>
public void Set(string strField, ProtectedString psNewValue)
{
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
Debug.Assert(psNewValue != null); if(psNewValue == null) throw new ArgumentNullException("psNewValue");
m_vStrings[strField] = psNewValue;
}
/// <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>
/// <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");
return m_vStrings.Remove(strField);
}
public List<string> GetKeys()
{
return new List<string>(m_vStrings.Keys);
}
public void EnableProtection(string strField, bool bProtect)
{
ProtectedString ps = Get(strField);
if(ps == null) return; // Nothing to do, no assert
if(ps.IsProtected != bProtect)
{
byte[] pbData = ps.ReadUtf8();
Set(strField, new ProtectedString(bProtect, pbData));
if(bProtect) MemUtil.ZeroByteArray(pbData);
}
}
}
}

View File

@@ -0,0 +1,373 @@
/*
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 KeePassLib.Interfaces;
namespace KeePassLib.Collections
{
/// <summary>
/// List of objects that implement <c>IDeepCloneable</c>,
/// and cannot be <c>null</c>.
/// </summary>
/// <typeparam name="T">Type specifier.</typeparam>
public sealed class PwObjectList<T> : IEnumerable<T>
where T : class, IDeepCloneable<T>
{
private List<T> m_vObjects = new List<T>();
/// <summary>
/// Get number of objects in this list.
/// </summary>
public uint UCount
{
get { return (uint)m_vObjects.Count; }
}
/// <summary>
/// Construct a new list of objects.
/// </summary>
public PwObjectList()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vObjects.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return m_vObjects.GetEnumerator();
}
public void Clear()
{
// Do not destroy contained objects!
m_vObjects.Clear();
}
/// <summary>
/// Clone the current <c>PwObjectList</c>, including all
/// stored objects (deep copy).
/// </summary>
/// <returns>New <c>PwObjectList</c>.</returns>
public PwObjectList<T> CloneDeep()
{
PwObjectList<T> pl = new PwObjectList<T>();
foreach(T po in m_vObjects)
pl.Add(po.CloneDeep());
return pl;
}
public PwObjectList<T> CloneShallow()
{
PwObjectList<T> tNew = new PwObjectList<T>();
foreach(T po in m_vObjects) tNew.Add(po);
return tNew;
}
public List<T> CloneShallowToList()
{
PwObjectList<T> tNew = CloneShallow();
return tNew.m_vObjects;
}
/// <summary>
/// Add an object to this list.
/// </summary>
/// <param name="pwObject">Object to be added.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public void Add(T pwObject)
{
Debug.Assert(pwObject != null);
if(pwObject == null) throw new ArgumentNullException("pwObject");
m_vObjects.Add(pwObject);
}
public void Add(PwObjectList<T> vObjects)
{
Debug.Assert(vObjects != null);
if(vObjects == null) throw new ArgumentNullException("vObjects");
foreach(T po in vObjects)
{
m_vObjects.Add(po);
}
}
public void Add(List<T> vObjects)
{
Debug.Assert(vObjects != null);
if(vObjects == null) throw new ArgumentNullException("vObjects");
foreach(T po in vObjects)
{
m_vObjects.Add(po);
}
}
public void Insert(uint uIndex, T pwObject)
{
Debug.Assert(pwObject != null);
if(pwObject == null) throw new ArgumentNullException("pwObject");
m_vObjects.Insert((int)uIndex, pwObject);
}
/// <summary>
/// Get an object of the list.
/// </summary>
/// <param name="uIndex">Index of the object to get. Must be valid, otherwise an
/// exception is thrown.</param>
/// <returns>Reference to an existing <c>T</c> object. Is never <c>null</c>.</returns>
public T GetAt(uint uIndex)
{
Debug.Assert(uIndex < m_vObjects.Count);
if(uIndex >= m_vObjects.Count) throw new ArgumentOutOfRangeException("uIndex");
return m_vObjects[(int)uIndex];
}
public void SetAt(uint uIndex, T pwObject)
{
Debug.Assert(pwObject != null);
if(pwObject == null) throw new ArgumentNullException("pwObject");
if(uIndex >= (uint)m_vObjects.Count)
throw new ArgumentOutOfRangeException("uIndex");
m_vObjects[(int)uIndex] = pwObject;
}
/// <summary>
/// Get a range of objects.
/// </summary>
/// <param name="uStartIndexIncl">Index of the first object to be
/// returned (inclusive).</param>
/// <param name="uEndIndexIncl">Index of the last object to be
/// returned (inclusive).</param>
/// <returns></returns>
public List<T> GetRange(uint uStartIndexIncl, uint uEndIndexIncl)
{
if(uStartIndexIncl >= (uint)m_vObjects.Count)
throw new ArgumentOutOfRangeException("uStartIndexIncl");
if(uEndIndexIncl >= (uint)m_vObjects.Count)
throw new ArgumentOutOfRangeException("uEndIndexIncl");
if(uStartIndexIncl > uEndIndexIncl)
throw new ArgumentException();
List<T> list = new List<T>((int)(uEndIndexIncl - uStartIndexIncl) + 1);
for(uint u = uStartIndexIncl; u <= uEndIndexIncl; ++u)
{
list.Add(m_vObjects[(int)u]);
}
return list;
}
public int IndexOf(T pwReference)
{
Debug.Assert(pwReference != null); if(pwReference == null) throw new ArgumentNullException("pwReference");
return m_vObjects.IndexOf(pwReference);
}
/// <summary>
/// Delete an object of this list. The object to be deleted is identified
/// by a reference handle.
/// </summary>
/// <param name="pwReference">Reference of the object to be deleted.</param>
/// <returns>Returns <c>true</c> if the object was deleted, <c>false</c> if
/// the object wasn't found in this list.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public bool Remove(T pwReference)
{
Debug.Assert(pwReference != null); if(pwReference == null) throw new ArgumentNullException("pwReference");
return m_vObjects.Remove(pwReference);
}
public void RemoveAt(uint uIndex)
{
m_vObjects.RemoveAt((int)uIndex);
}
/// <summary>
/// Move an object up or down.
/// </summary>
/// <param name="tObject">The object to be moved.</param>
/// <param name="bUp">Move one up. If <c>false</c>, move one down.</param>
public void MoveOne(T tObject, bool bUp)
{
Debug.Assert(tObject != null);
if(tObject == null) throw new ArgumentNullException("tObject");
int nCount = m_vObjects.Count;
if(nCount <= 1) return;
int nIndex = m_vObjects.IndexOf(tObject);
if(nIndex < 0) { Debug.Assert(false); return; }
if(bUp && (nIndex > 0)) // No assert for top item
{
T tTemp = m_vObjects[nIndex - 1];
m_vObjects[nIndex - 1] = m_vObjects[nIndex];
m_vObjects[nIndex] = tTemp;
}
else if(!bUp && (nIndex != (nCount - 1))) // No assert for bottom item
{
T tTemp = m_vObjects[nIndex + 1];
m_vObjects[nIndex + 1] = m_vObjects[nIndex];
m_vObjects[nIndex] = tTemp;
}
}
public void MoveOne(T[] vObjects, bool bUp)
{
Debug.Assert(vObjects != null);
if(vObjects == null) throw new ArgumentNullException("vObjects");
/// <summary>
List<int> lIndices = new List<int>();
foreach(T t in vObjects)
{
if(t == null) { Debug.Assert(false); continue; }
/// Move some of the objects in this list to the top/bottom.
int p = IndexOf(t);
if(p >= 0) lIndices.Add(p);
else { Debug.Assert(false); }
}
/// </summary>
MoveOne(lIndices.ToArray(), bUp);
}
/// <param name="vObjects">List of objects to be moved.</param>
public void MoveOne(int[] vIndices, bool bUp)
{
Debug.Assert(vIndices != null);
if(vIndices == null) throw new ArgumentNullException("vIndices");
/// <param name="bTop">Move to top. If <c>false</c>, move to bottom.</param>
int n = m_vObjects.Count;
if(n <= 1) return; // No moving possible
int m = vIndices.Length;
if(m == 0) return; // Nothing to move
int[] v = new int[m];
Array.Copy(vIndices, v, m);
Array.Sort<int>(v);
if((bUp && (v[0] <= 0)) || (!bUp && (v[m - 1] >= (n - 1))))
return; // Moving as a block is not possible
int iStart = (bUp ? 0 : (m - 1));
int iExcl = (bUp ? m : -1);
int iStep = (bUp ? 1 : -1);
for(int i = iStart; i != iExcl; i += iStep)
{
int p = v[i];
if((p < 0) || (p >= n)) { Debug.Assert(false); continue; }
T t = m_vObjects[p];
if(bUp)
{
Debug.Assert(p > 0);
m_vObjects.RemoveAt(p);
m_vObjects.Insert(p - 1, t);
}
else // Down
{
Debug.Assert(p < (n - 1));
m_vObjects.RemoveAt(p);
m_vObjects.Insert(p + 1, t);
}
}
}
/// <summary>
/// Move some of the objects in this list to the top/bottom.
/// </summary>
/// <param name="vObjects">List of objects to be moved.</param>
/// <param name="bTop">Move to top. If <c>false</c>, move to bottom.</param>
public void MoveTopBottom(T[] vObjects, bool bTop)
{
Debug.Assert(vObjects != null);
if(vObjects == null) throw new ArgumentNullException("vObjects");
if(vObjects.Length == 0) return;
int nCount = m_vObjects.Count;
foreach(T t in vObjects) m_vObjects.Remove(t);
if(bTop)
{
int nPos = 0;
foreach(T t in vObjects)
{
m_vObjects.Insert(nPos, t);
++nPos;
}
}
else // Move to bottom
{
foreach(T t in vObjects) m_vObjects.Add(t);
}
Debug.Assert(nCount == m_vObjects.Count);
if(nCount != m_vObjects.Count)
throw new ArgumentException("At least one of the T objects in the vObjects list doesn't exist!");
}
public void Sort(IComparer<T> tComparer)
{
if(tComparer == null) throw new ArgumentNullException("tComparer");
m_vObjects.Sort(tComparer);
}
public static PwObjectList<T> FromArray(T[] tArray)
{
if(tArray == null) throw new ArgumentNullException("tArray");
PwObjectList<T> l = new PwObjectList<T>();
foreach(T t in tArray) { l.Add(t); }
return l;
}
public static PwObjectList<T> FromList(List<T> tList)
{
if(tList == null) throw new ArgumentNullException("tList");
PwObjectList<T> l = new PwObjectList<T>();
l.Add(tList);
return l;
}
}
}

View File

@@ -0,0 +1,232 @@
/*
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 KeePassLib.Delegates;
using KeePassLib.Interfaces;
using KeePassLib.Utility;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace KeePassLib.Collections
{
public sealed class PwObjectPool
{
private SortedDictionary<PwUuid, IStructureItem> m_dict =
new SortedDictionary<PwUuid, IStructureItem>();
public static PwObjectPool FromGroupRecursive(PwGroup pgRoot, bool bEntries)
{
if(pgRoot == null) throw new ArgumentNullException("pgRoot");
PwObjectPool p = new PwObjectPool();
if(!bEntries) p.m_dict[pgRoot.Uuid] = pgRoot;
GroupHandler gh = delegate(PwGroup pg)
{
p.m_dict[pg.Uuid] = pg;
return true;
};
EntryHandler eh = delegate(PwEntry pe)
{
p.m_dict[pe.Uuid] = pe;
return true;
};
pgRoot.TraverseTree(TraversalMethod.PreOrder, bEntries ? null : gh,
bEntries ? eh : null);
return p;
}
public IStructureItem Get(PwUuid pwUuid)
{
IStructureItem pItem;
m_dict.TryGetValue(pwUuid, out pItem);
return pItem;
}
public bool ContainsOnlyType(Type t)
{
foreach(KeyValuePair<PwUuid, IStructureItem> kvp in m_dict)
{
if(kvp.Value.GetType() != t) return false;
}
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

@@ -0,0 +1,170 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 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 KeePassLib.Interfaces;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace KeePassLib.Collections
{
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
{
private SortedDictionary<string, string> m_d =
new SortedDictionary<string, string>();
// Non-null if and only if last mod. times should be remembered
private Dictionary<string, DateTime> m_dLastMod = null;
public int Count
{
get { return m_d.Count; }
}
public StringDictionaryEx()
{
}
internal StringDictionaryEx(bool bRememberLastMod)
{
if (bRememberLastMod) m_dLastMod = new Dictionary<string, DateTime>();
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_d.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return m_d.GetEnumerator();
}
public StringDictionaryEx CloneDeep()
{
StringDictionaryEx sdNew = new StringDictionaryEx();
foreach (KeyValuePair<string, string> kvp in m_d)
sdNew.m_d[kvp.Key] = kvp.Value;
if (m_dLastMod != null)
sdNew.m_dLastMod = new Dictionary<string, DateTime>(m_dLastMod);
Debug.Assert(Equals(sdNew));
return sdNew;
}
public bool Equals(StringDictionaryEx sdOther)
{
if (sdOther == null) { Debug.Assert(false); return false; }
if (m_d.Count != sdOther.m_d.Count) return false;
foreach (KeyValuePair<string, string> kvp in sdOther.m_d)
{
string str = Get(kvp.Key);
if ((str == null) || (str != kvp.Value)) return false;
}
int cLastModT = ((m_dLastMod != null) ? m_dLastMod.Count : -1);
int cLastModO = ((sdOther.m_dLastMod != null) ? sdOther.m_dLastMod.Count : -1);
if (cLastModT != cLastModO) return false;
if (m_dLastMod != null)
{
foreach (KeyValuePair<string, DateTime> kvp in sdOther.m_dLastMod)
{
DateTime? odt = GetLastModificationTime(kvp.Key);
if (!odt.HasValue) return false;
if (odt.Value != kvp.Value) return false;
}
}
return true;
}
public string Get(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
string str;
m_d.TryGetValue(strName, out str);
return str;
}
internal DateTime? GetLastModificationTime(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (m_dLastMod == null) return null;
DateTime dt;
if (m_dLastMod.TryGetValue(strName, out dt)) return dt;
return null;
}
public bool Exists(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
return m_d.ContainsKey(strName);
}
public void Set(string strName, string strValue)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
m_d[strName] = strValue;
if (m_dLastMod != null) m_dLastMod[strName] = DateTime.UtcNow;
}
internal void Set(string strName, string strValue, DateTime? odtLastMod)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
m_d[strName] = strValue;
if (m_dLastMod != null)
{
if (odtLastMod.HasValue) m_dLastMod[strName] = odtLastMod.Value;
else m_dLastMod.Remove(strName);
}
}
public bool Remove(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (m_dLastMod != null) m_dLastMod.Remove(strName);
return m_d.Remove(strName);
}
}
}

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 KeePassLib.Resources;
using KeePassLib.Utility;
namespace KeePassLib.Collections
{
public class VariantDictionary : ICloneable
{
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);
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));
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 KeePassLib.Resources;
using KeePassLib.Utility;
namespace KeePassLib.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,191 @@
/*
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 KeePassLib.Resources;
namespace KeePassLib.Cryptography.Cipher
{
public sealed class ChaCha20Engine : ICipherEngine2
{
private static PwUuid m_uuid = null;
internal static PwUuid ChaCha20Uuid
{
get
{
PwUuid pu = m_uuid;
if (pu == null)
{
pu = new PwUuid(new byte[] {
0xD6, 0x03, 0x8A, 0x2B, 0x8B, 0x6F, 0x4C, 0xB5,
0xA5, 0x24, 0x33, 0x9A, 0x31, 0xDB, 0xB5, 0x9A });
m_uuid = pu;
}
return pu;
}
}
public PwUuid CipherUuid
{
get { return ChaCha20Uuid; }
}
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.Close();
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

@@ -0,0 +1,171 @@
/*
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.Text;
using System.IO;
using System.Diagnostics;
namespace KeePassLib.Cryptography.Cipher
{
/// <summary>
/// Pool of encryption/decryption algorithms (ciphers).
/// </summary>
public sealed class CipherPool
{
private List<ICipherEngine> m_vCiphers = new List<ICipherEngine>();
private static CipherPool m_poolGlobal = null;
/// <summary>
/// Reference to the global cipher pool.
/// </summary>
public static CipherPool GlobalPool
{
get
{
CipherPool cp = m_poolGlobal;
if(cp == null)
{
cp = new CipherPool();
cp.AddCipher(new StandardAesEngine());
cp.AddCipher(new ChaCha20Engine());
m_poolGlobal = cp;
}
return cp;
}
}
/// <summary>
/// Remove all cipher engines from the current pool.
/// </summary>
public void Clear()
{
m_vCiphers.Clear();
}
/// <summary>
/// Add a cipher engine to the pool.
/// </summary>
/// <param name="csEngine">Cipher engine to add. Must not be <c>null</c>.</param>
public void AddCipher(ICipherEngine csEngine)
{
Debug.Assert(csEngine != null);
if(csEngine == null) throw new ArgumentNullException("csEngine");
// Return if a cipher with that ID is registered already.
for(int i = 0; i < m_vCiphers.Count; ++i)
if(m_vCiphers[i].CipherUuid.Equals(csEngine.CipherUuid))
return;
m_vCiphers.Add(csEngine);
}
/// <summary>
/// Get a cipher identified by its UUID.
/// </summary>
/// <param name="uuidCipher">UUID of the cipher to return.</param>
/// <returns>Reference to the requested cipher. If the cipher is
/// not found, <c>null</c> is returned.</returns>
public ICipherEngine GetCipher(PwUuid uuidCipher)
{
foreach(ICipherEngine iEngine in m_vCiphers)
{
if(iEngine.CipherUuid.Equals(uuidCipher))
return iEngine;
}
return null;
}
/// <summary>
/// Get the index of a cipher. This index is temporary and should
/// not be stored or used to identify a cipher.
/// </summary>
/// <param name="uuidCipher">UUID of the cipher.</param>
/// <returns>Index of the requested cipher. Returns <c>-1</c> if
/// the specified cipher is not found.</returns>
public int GetCipherIndex(PwUuid uuidCipher)
{
for(int i = 0; i < m_vCiphers.Count; ++i)
{
if(m_vCiphers[i].CipherUuid.Equals(uuidCipher))
return i;
}
Debug.Assert(false);
return -1;
}
/// <summary>
/// Get the index of a cipher. This index is temporary and should
/// not be stored or used to identify a cipher.
/// </summary>
/// <param name="strDisplayName">Name of the cipher. Note that
/// multiple ciphers can have the same name. In this case, the
/// first matching cipher is returned.</param>
/// <returns>Cipher with the specified name or <c>-1</c> if
/// no cipher with that name is found.</returns>
public int GetCipherIndex(string strDisplayName)
{
for(int i = 0; i < m_vCiphers.Count; ++i)
if(m_vCiphers[i].DisplayName == strDisplayName)
return i;
Debug.Assert(false);
return -1;
}
/// <summary>
/// Get the number of cipher engines in this pool.
/// </summary>
public int EngineCount
{
get { return m_vCiphers.Count; }
}
/// <summary>
/// Get the cipher engine at the specified position. Throws
/// an exception if the index is invalid. You can use this
/// to iterate over all ciphers, but do not use it to
/// identify ciphers.
/// </summary>
/// <param name="nIndex">Index of the requested cipher engine.</param>
/// <returns>Reference to the cipher engine at the specified
/// position.</returns>
public ICipherEngine this[int nIndex]
{
get
{
if((nIndex < 0) || (nIndex >= m_vCiphers.Count))
throw new ArgumentOutOfRangeException("nIndex");
return m_vCiphers[nIndex];
}
}
public IEnumerable<ICipherEngine> Engines
{
get {
return m_vCiphers;
}
}
}
}

View File

@@ -0,0 +1,104 @@
/*
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 KeePassLib.Utility;
namespace KeePassLib.Cryptography.Cipher
{
public abstract class CtrBlockCipher : IDisposable
{
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;
}
}
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 == 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

@@ -0,0 +1,87 @@
/*
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.IO;
namespace KeePassLib.Cryptography.Cipher
{
/// <summary>
/// Interface of an encryption/decryption class.
/// </summary>
public interface ICipherEngine
{
/// <summary>
/// UUID of the engine. If you want to write an engine/plugin,
/// please contact the KeePass team to obtain a new UUID.
/// </summary>
PwUuid CipherUuid
{
get;
}
/// <summary>
/// String displayed in the list of available encryption/decryption
/// engines in the GUI.
/// </summary>
string DisplayName
{
get;
}
/// <summary>
/// Encrypt a stream.
/// </summary>
/// <param name="sPlainText">Stream to read the plain-text from.</param>
/// <param name="pbKey">Key to use.</param>
/// <param name="pbIV">Initialization vector.</param>
/// <returns>Stream, from which the encrypted data can be read.</returns>
Stream EncryptStream(Stream sPlainText, byte[] pbKey, byte[] pbIV);
/// <summary>
/// Decrypt a stream.
/// </summary>
/// <param name="sEncrypted">Stream to read the encrypted data from.</param>
/// <param name="pbKey">Key to use.</param>
/// <param name="pbIV">Initialization vector.</param>
/// <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

@@ -0,0 +1,165 @@
/*
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
*/
// Implementation of the Salsa20 cipher, based on the eSTREAM submission.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.Cipher
{
public sealed class Salsa20Cipher : CtrBlockCipher
{
private uint[] m_s = new uint[16]; // State
private uint[] m_x = new uint[16]; // Working buffer
private static readonly uint[] g_sigma = new uint[4] {
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
};
public override int BlockSize
{
get { return 64; }
}
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8) : base()
{
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");
// Clear sensitive data
// 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
}
protected override void Dispose(bool bDisposing)
{
if(bDisposing)
{
MemUtil.ZeroArray<uint>(m_s);
MemUtil.ZeroArray<uint>(m_x);
}
base.Dispose(bDisposing);
}
// Compiler/runtime might remove array bound checks after this
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) // (int i = 20; i > 0; i -= 2)
{
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] += 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[8];
if(s[8] == 0) ++s[9];
}
}
}
}

View File

@@ -0,0 +1,157 @@
/*
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.Text;
using System.IO;
using System.Security;
using System.Diagnostics;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Resources;
namespace KeePassLib.Cryptography.Cipher
{
/// <summary>
/// Standard AES cipher implementation.
/// </summary>
public sealed class StandardAesEngine : ICipherEngine
{
#if !KeePassUAP
private const CipherMode m_rCipherMode = CipherMode.CBC;
private const PaddingMode m_rCipherPadding = PaddingMode.PKCS7;
#endif
private static PwUuid g_uuidAes = null;
/// <summary>
/// UUID of the cipher engine. This ID uniquely identifies the
/// AES engine. Must not be used by other ciphers.
/// </summary>
public static PwUuid AesUuid
{
get
{
PwUuid pu = g_uuidAes;
if(pu == null)
{
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 pu;
}
}
/// <summary>
/// Get the UUID of this cipher engine as <c>PwUuid</c> object.
/// </summary>
public PwUuid CipherUuid
{
get { return StandardAesEngine.AesUuid; }
}
/// <summary>
/// Get a displayable name describing this cipher engine.
/// </summary>
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)
{
Debug.Assert(stream != null); if(stream == null) throw new ArgumentNullException("stream");
Debug.Assert(pbKey != null); if(pbKey == null) throw new ArgumentNullException("pbKey");
Debug.Assert(pbKey.Length == 32);
if(pbKey.Length != 32) throw new ArgumentException("Key must be 256 bits wide!");
Debug.Assert(pbIV != null); if(pbIV == null) throw new ArgumentNullException("pbIV");
Debug.Assert(pbIV.Length == 16);
if(pbIV.Length != 16) throw new ArgumentException("Initialization vector must be 128 bits wide!");
if(bEncrypt)
{
Debug.Assert(stream.CanWrite);
if(!stream.CanWrite) throw new ArgumentException("Stream must be writable!");
}
else // Decrypt
{
Debug.Assert(stream.CanRead);
if(!stream.CanRead) throw new ArgumentException("Encrypted stream must be readable!");
}
}
private static Stream CreateStream(Stream s, bool bEncrypt, byte[] pbKey, byte[] 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 KeePassUAP
return StandardAesEngineExt.CreateStream(s, bEncrypt, pbLocalKey, pbLocalIV);
#else
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbLocalIV;
r.KeySize = 256;
r.Key = pbLocalKey;
r.Mode = m_rCipherMode;
r.Padding = m_rCipherPadding;
ICryptoTransform iTransform = (bEncrypt ? r.CreateEncryptor() : r.CreateDecryptor());
Debug.Assert(iTransform != null);
if(iTransform == null) throw new SecurityException("Unable to create Rijndael transform!");
return new CryptoStream(s, iTransform, bEncrypt ? CryptoStreamMode.Write :
CryptoStreamMode.Read);
#endif
}
public Stream EncryptStream(Stream sPlainText, byte[] pbKey, byte[] pbIV)
{
return StandardAesEngine.CreateStream(sPlainText, true, pbKey, pbIV);
}
public Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV)
{
return StandardAesEngine.CreateStream(sEncrypted, false, pbKey, pbIV);
}
}
}

View File

@@ -0,0 +1,393 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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.Globalization;
using System.IO;
#if !KeePassUAP
using System.Drawing;
using System.Security.Cryptography;
#endif
using KeePassLib.Native;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
/// <summary>
/// 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 ulong m_uCounter;
private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
private ulong m_uGeneratedBytesCount = 0;
private static object g_oSyncRoot = new object();
private object m_oSyncRoot = new object();
private static CryptoRandom g_pInstance = null;
public static CryptoRandom Instance
{
get
{
CryptoRandom cr;
lock(g_oSyncRoot)
{
cr = g_pInstance;
if(cr == null)
{
cr = new CryptoRandom();
g_pInstance = cr;
}
}
return cr;
}
}
/// <summary>
/// Get the number of random bytes that this instance generated so far.
/// Note that this number can be higher than the number of random bytes
/// actually requested using the <c>GetRandomBytes</c> method.
/// </summary>
public ulong GeneratedBytesCount
{
get
{
ulong u;
lock(m_oSyncRoot) { u = m_uGeneratedBytesCount; }
return u;
}
}
/// <summary>
/// Event that is triggered whenever the internal <c>GenerateRandom256</c>
/// method is called to generate random bytes.
/// </summary>
public event EventHandler GenerateRandom256Pre;
private CryptoRandom()
{
// byte[] pb = new byte[8];
// rWeak.NextBytes(pb);
// m_uCounter = MemUtil.BytesToUInt64(pb);
m_uCounter = (ulong)DateTime.UtcNow.ToBinary();
AddEntropy(GetSystemData());
AddEntropy(GetCspData());
}
/// <summary>
/// Update the internal seed of the random number generator based
/// on entropy data.
/// This method is thread-safe.
/// </summary>
/// <param name="pbEntropy">Entropy bytes.</param>
public void AddEntropy(byte[] pbEntropy)
{
if(pbEntropy == null) { Debug.Assert(false); return; }
if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
byte[] pbNewData = pbEntropy;
if(pbEntropy.Length > 64)
{
#if KeePassLibSD
using(SHA256Managed shaNew = new SHA256Managed())
#else
using(SHA512Managed shaNew = new SHA512Managed())
#endif
{
pbNewData = shaNew.ComputeHash(pbEntropy);
}
}
lock(m_oSyncRoot)
{
int cbPool = m_pbEntropyPool.Length;
int cbNew = pbNewData.Length;
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
using(SHA512Managed shaPool = new SHA512Managed())
#endif
{
m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
}
MemUtil.ZeroByteArray(pbCmp);
}
}
private static byte[] GetSystemData()
{
MemoryStream ms = new MemoryStream();
byte[] pb;
pb = MemUtil.Int32ToBytes(Environment.TickCount);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(DateTime.UtcNow.ToBinary());
MemUtil.Write(ms, pb);
#if !KeePassLibSD
/*Not supported on Android
// In try-catch for systems without GUI;
// https://sourceforge.net/p/keepass/discussion/329221/thread/20335b73/
try
{
Point pt = Cursor.Position;
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)NativeLib.GetPlatformID());
MemUtil.Write(ms, pb);
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
{
p = Process.GetCurrentProcess();
// Not supported in Mono 1.2.6:
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);
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
// ms.Write(pb, 0, pb.Length);
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
// MemUtil.Write(ms, pb);
}
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
finally
{
try { if(p != null) p.Dispose(); }
catch(Exception) { Debug.Assert(false); }
}
#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();
MemUtil.Write(ms, pb);
byte[] pbAll = ms.ToArray();
ms.Close();
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);
return pbCspRandom;
}
private byte[] GenerateRandom256()
{
if(this.GenerateRandom256Pre != null)
this.GenerateRandom256Pre(this, EventArgs.Empty);
byte[] pbCmp;
lock(m_oSyncRoot)
{
m_uCounter += 0x74D8B29E4D38E161UL; // Prime number
byte[] pbCounter = MemUtil.UInt64ToBytes(m_uCounter);
byte[] pbCspRandom = GetCspData();
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;
}
byte[] pbRet = CryptoUtil.HashSha256(pbCmp);
MemUtil.ZeroByteArray(pbCmp);
return pbRet;
}
/// <summary>
/// Get a number of cryptographically strong random bytes.
/// This method is thread-safe.
/// </summary>
/// <param name="uRequestedBytes">Number of requested random bytes.</param>
/// <returns>A byte array consisting of <paramref name="uRequestedBytes" />
/// random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedBytes)
{
if(uRequestedBytes == 0) return MemUtil.EmptyByteArray;
if(uRequestedBytes > (uint)int.MaxValue)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("uRequestedBytes");
}
int cbRem = (int)uRequestedBytes;
byte[] pbRes = new byte[cbRem];
int iPos = 0;
while(cbRem != 0)
{
byte[] pbRandom256 = GenerateRandom256();
Debug.Assert(pbRandom256.Length == 32);
int cbCopy = Math.Min(cbRem, pbRandom256.Length);
Array.Copy(pbRandom256, 0, pbRes, iPos, cbCopy);
MemUtil.ZeroByteArray(pbRandom256);
iPos += cbCopy;
cbRem -= cbCopy;
}
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

@@ -0,0 +1,258 @@
/*
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.Diagnostics;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
/// <summary>
/// Algorithms supported by <c>CryptoRandomStream</c>.
/// </summary>
public enum CrsAlgorithm
{
/// <summary>
/// Not supported.
/// </summary>
Null = 0,
/// <summary>
/// A variant of the ARCFour algorithm (RC4 incompatible).
/// </summary>
/// </summary>
ArcFourVariant = 1,
/// <summary>
/// Salsa20 stream cipher algorithm.
/// </summary>
Salsa20 = 2,
/// <summary>
/// ChaCha20 stream cipher algorithm.
/// </summary>
ChaCha20 = 3,
Count = 4
}
/// <summary>
/// A random stream class. The class is initialized using random
/// bytes provided by the caller. The produced stream has random
/// 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 : IDisposable
{
private readonly CrsAlgorithm m_crsAlgorithm;
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="pbKey">Initialization key. Must not be <c>null</c> and
/// must contain at least 1 byte.</param>
public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
{
if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
/// <exception cref="System.ArgumentNullException">Thrown if the
int cbKey = pbKey.Length;
if(cbKey <= 0)
{
Debug.Assert(false); // Need at least one byte
throw new ArgumentOutOfRangeException("pbKey");
}
/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception>
m_crsAlgorithm = a;
/// <exception cref="System.ArgumentException">Thrown if the
if(a == CrsAlgorithm.ChaCha20)
{
byte[] pbKey32 = new byte[32];
byte[] pbIV12 = new byte[12];
/// <paramref name="pbKey" /> parameter contains no bytes or the
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);
}
/// algorithm is unknown.</exception>
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(int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
unchecked
{
byte j = 0, t;
int inxKey = 0;
for(int w = 0; w < 256; ++w) // Key setup
{
j += (byte)(m_pbState[w] + pbKey[inxKey]);
t = m_pbState[0]; // Swap entries
m_pbState[0] = m_pbState[j];
m_pbState[j] = t;
++inxKey;
if(inxKey >= cbKey) inxKey = 0;
}
}
GetRandomBytes(512); // Increases security, see cryptanalysis
}
else // Unknown algorithm
{
Debug.Assert(false);
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); }
}
}
/// <summary>
/// Get <paramref name="uRequestedCount" /> random bytes.
/// </summary>
/// <param name="uRequestedCount">Number of random bytes to retrieve.</param>
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedCount)
{
if(uRequestedCount == 0) return MemUtil.EmptyByteArray;
if(uRequestedCount > (uint)int.MaxValue)
throw new ArgumentOutOfRangeException("uRequestedCount");
int cb = (int)uRequestedCount;
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(int w = 0; w < cb; ++w)
{
++m_i;
m_j += m_pbState[m_i];
byte t = m_pbState[m_i]; // Swap entries
m_pbState[m_i] = m_pbState[m_j];
m_pbState[m_j] = t;
t = (byte)(m_pbState[m_i] + m_pbState[m_j]);
pbRet[w] = m_pbState[t];
}
}
}
else { Debug.Assert(false); }
return pbRet;
}
public ulong GetRandomUInt64()
{
byte[] pb = GetRandomBytes(8);
return MemUtil.BytesToUInt64(pb);
}
#if CRSBENCHMARK
public static string Benchmark()
{
int nRounds = 2000000;
string str = "ArcFour small: " + BenchTime(CrsAlgorithm.ArcFourVariant,
nRounds, 16).ToString() + "\r\n";
str += "ArcFour big: " + BenchTime(CrsAlgorithm.ArcFourVariant,
32, 2 * 1024 * 1024).ToString() + "\r\n";
str += "Salsa20 small: " + BenchTime(CrsAlgorithm.Salsa20,
nRounds, 16).ToString() + "\r\n";
str += "Salsa20 big: " + BenchTime(CrsAlgorithm.Salsa20,
32, 2 * 1024 * 1024).ToString();
return str;
}
private static int BenchTime(CrsAlgorithm cra, int nRounds, int nDataSize)
{
byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 };
int nStart = Environment.TickCount;
for(int i = 0; i < nRounds; ++i)
{
using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey))
{
c.GetRandomBytes((uint)nDataSize);
}
}
int nEnd = Environment.TickCount;
return (nEnd - nStart);
}
#endif
}
}

View File

@@ -0,0 +1,254 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2020 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.Reflection;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Native;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
public static class CryptoUtil
{
private static bool? g_obProtData = null;
public static bool IsProtectedDataSupported
{
get
{
if (g_obProtData.HasValue) return g_obProtData.Value;
bool b = false;
try
{
Random r = CryptoRandom.NewWeakRandom();
byte[] pbData = new byte[137];
r.NextBytes(pbData);
byte[] pbEnt = new byte[41];
r.NextBytes(pbEnt);
byte[] pbEnc = ProtectedData.Protect(pbData, pbEnt,
DataProtectionScope.CurrentUser);
if ((pbEnc != null) && !MemUtil.ArraysEqual(pbEnc, pbData))
{
byte[] pbDec = ProtectedData.Unprotect(pbEnc, pbEnt,
DataProtectionScope.CurrentUser);
if ((pbDec != null) && MemUtil.ArraysEqual(pbDec, pbData))
b = true;
}
}
catch (Exception) { Debug.Assert(false); }
Debug.Assert(b); // Should be supported on all systems
g_obProtData = b;
return b;
}
}
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;
}
internal static byte[] HashSha256(string strFilePath)
{
byte[] pbHash = null;
using (FileStream fs = new FileStream(strFilePath, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
using (SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(fs);
}
}
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 !KeePassUAP
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
public static byte[] ProtectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, true, pbOptEntropy, s);
}
public static byte[] UnprotectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, false, pbOptEntropy, s);
}
private static byte[] ProtectDataPriv(byte[] pb, bool bProtect,
byte[] pbOptEntropy, DataProtectionScope s)
{
if (pb == null) throw new ArgumentNullException("pb");
if ((pbOptEntropy != null) && (pbOptEntropy.Length == 0))
pbOptEntropy = null;
if (CryptoUtil.IsProtectedDataSupported)
{
if (bProtect)
return ProtectedData.Protect(pb, pbOptEntropy, s);
return ProtectedData.Unprotect(pb, pbOptEntropy, s);
}
Debug.Assert(false);
byte[] pbCopy = new byte[pb.Length];
Array.Copy(pb, pbCopy, pb.Length);
return pbCopy;
}
}
}

View File

@@ -0,0 +1,232 @@
/*
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.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.Hash
{
public sealed class Blake2b : HashAlgorithm
{
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 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 override 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];
}
protected override 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;
}
}
protected override 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;
}
}
}

View File

@@ -0,0 +1,184 @@
/*
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.Text;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
public sealed class HashingStreamEx : Stream
{
private readonly Stream m_sBaseStream;
private bool m_bWriting;
private HashAlgorithm m_hash;
private byte[] m_pbFinalHash = null;
public byte[] Hash
{
get { return m_pbFinalHash; }
}
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 { return m_sBaseStream.Length; }
}
public override long Position
{
get { return m_sBaseStream.Position; }
set { throw new NotSupportedException(); }
}
public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm)
{
if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
m_sBaseStream = sBaseStream;
m_bWriting = bWriting;
#if !KeePassLibSD
m_hash = (hashAlgorithm ?? new SHA256Managed());
#else // KeePassLibSD
m_hash = null;
try { m_hash = HashAlgorithm.Create("SHA256"); }
catch(Exception) { }
try { if(m_hash == null) m_hash = HashAlgorithm.Create(); }
catch(Exception) { }
#endif
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 DEBUG
MessageService.ShowWarning("Broken HashAlgorithm object in HashingStreamEx.");
#endif
m_hash = null;
}
}
protected override void Dispose(bool disposing)
{
if(disposing)
{
if(m_hash != null)
{
try
{
m_hash.TransformFinalBlock(new byte[0], 0, 0);
m_pbFinalHash = m_hash.Hash;
}
catch(Exception) { Debug.Assert(false); }
m_hash = null;
}
m_sBaseStream.Close();
}
base.Dispose(disposing);
}
public override void Flush()
{
m_sBaseStream.Flush();
}
public override long Seek(long lOffset, SeekOrigin soOrigin)
{
throw new NotSupportedException();
}
public override void SetLength(long lValue)
{
throw new NotSupportedException();
}
public override int Read(byte[] pbBuffer, int nOffset, int nCount)
{
if(m_bWriting) throw new InvalidOperationException();
int nRead = m_sBaseStream.Read(pbBuffer, nOffset, nCount);
int nPartialRead = nRead;
while((nRead < nCount) && (nPartialRead != 0))
{
nPartialRead = m_sBaseStream.Read(pbBuffer, nOffset + nRead,
nCount - nRead);
nRead += nPartialRead;
}
#if DEBUG
byte[] pbOrg = new byte[pbBuffer.Length];
Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
#endif
if((m_hash != null) && (nRead > 0))
m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset);
#if DEBUG
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
#endif
return nRead;
}
public override void Write(byte[] pbBuffer, int nOffset, int nCount)
{
if(!m_bWriting) throw new InvalidOperationException();
#if DEBUG
byte[] pbOrg = new byte[pbBuffer.Length];
Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
#endif
if((m_hash != null) && (nCount > 0))
m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset);
#if DEBUG
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
#endif
m_sBaseStream.Write(pbBuffer, nOffset, nCount);
}
}
}

View File

@@ -0,0 +1,92 @@
/*
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.Text;
using System.Security.Cryptography;
using System.Globalization;
using KeePassLib.Utility;
#if (!KeePassLibSD && !KeePassRT)
namespace KeePassLib.Cryptography
{
/// <summary>
/// Generate HMAC-based one-time passwords as specified in RFC 4226.
/// </summary>
public static class HmacOtp
{
private static readonly uint[] vDigitsPower = new uint[]{ 1, 10, 100,
1000, 10000, 100000, 1000000, 10000000, 100000000 };
public static string Generate(byte[] pbSecret, ulong uFactor,
uint uCodeDigits, bool bAddChecksum, int iTruncationOffset)
{
byte[] pbText = MemUtil.UInt64ToBytes(uFactor);
Array.Reverse(pbText); // Big-Endian
HMACSHA1 hsha1 = new HMACSHA1(pbSecret);
byte[] pbHash = hsha1.ComputeHash(pbText);
uint uOffset = (uint)(pbHash[pbHash.Length - 1] & 0xF);
if((iTruncationOffset >= 0) && (iTruncationOffset < (pbHash.Length - 4)))
uOffset = (uint)iTruncationOffset;
uint uBinary = (uint)(((pbHash[uOffset] & 0x7F) << 24) |
((pbHash[uOffset + 1] & 0xFF) << 16) |
((pbHash[uOffset + 2] & 0xFF) << 8) |
(pbHash[uOffset + 3] & 0xFF));
uint uOtp = (uBinary % vDigitsPower[uCodeDigits]);
if(bAddChecksum)
uOtp = ((uOtp * 10) + CalculateChecksum(uOtp, uCodeDigits));
uint uDigits = (bAddChecksum ? (uCodeDigits + 1) : uCodeDigits);
return uOtp.ToString(NumberFormatInfo.InvariantInfo).PadLeft(
(int)uDigits, '0');
}
private static readonly uint[] vDoubleDigits = new uint[]{ 0, 2, 4, 6, 8,
1, 3, 5, 7, 9 };
private static uint CalculateChecksum(uint uNum, uint uDigits)
{
bool bDoubleDigit = true;
uint uTotal = 0;
while(0 < uDigits--)
{
uint uDigit = (uNum % 10);
uNum /= 10;
if(bDoubleDigit) uDigit = vDoubleDigits[uDigit];
uTotal += uDigit;
bDoubleDigit = !bDoubleDigit;
}
uint uResult = (uTotal % 10);
if(uResult != 0) uResult = 10 - uResult;
return uResult;
}
}
}
#endif

View File

@@ -0,0 +1,281 @@
/*
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 keepass2android;
#if KeePassUAP
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
#else
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography;
using KeePassLib.Native;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.KeyDerivation
{
public sealed 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]
public override PwUuid Uuid
{
get { return g_uuid; }
}
public override string Name
{
get { return "AES-KDF"; }
}
public override byte[] GetSeed(KdfParameters p)
{ return p.GetByteArray(ParamSeed); }
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
{
// Try to use the native library first
if (NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds))
{
//no need to hash, this is already done in the native library.
byte[] pbKey = new byte[32];
Array.Copy(pbNewKey, pbKey, pbNewKey.Length);
return pbKey;
}
if(TransformKeyManaged(pbNewKey, pbKeySeed32, uNumRounds))
return CryptoUtil.HashSha256(pbNewKey);
}
finally { MemUtil.ZeroByteArray(pbNewKey); }
return null;
}
public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
Kp2aLog.Log("Warning: transforming key managed. Expect this to be slow!");
#if 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);
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbIV;
r.Mode = CipherMode.ECB;
r.KeySize = 256;
r.Key = pbKeySeed32;
ICryptoTransform iCrypt = r.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)
{
const ulong uStep = 3001;
ulong uRounds;
KdfParameters p = GetDefaultParameters();
// Try native method
if(NativeLib.TransformKeyBenchmark256(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 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);
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbIV;
r.Mode = CipherMode.ECB;
r.KeySize = 256;
r.Key = pbKey;
ICryptoTransform iCrypt = r.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 < uStep; ++j)
{
#if 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 += uStep;
if(uRounds < uStep) // 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,682 @@
/*
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 KeePassLib.Cryptography.Hash;
using KeePassLib.Utility;
namespace KeePassLib.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 ulong NbAddressesInBlock = 128;
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 Argon2Type Type = Argon2Type.D;
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.Close();
this.Finished = null;
}
else { Debug.Assert(false); }
}
}
private byte[] Argon2Transform(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.Type = m_t;
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 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((uint)m_t, pbBuf, 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 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(!ThreadPool.QueueUserWorkItem(FillSegmentThr, ti))
{
Debug.Assert(false);
throw new OutOfMemoryException();
}
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[] pbR = new ulong[NbBlockSizeInQW];
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
ulong[] pbAddrInputZero = null;
bool bDataIndependentAddr = ((ctx.Type == Argon2Type.ID) &&
(ti.Pass == 0) && (ti.Slice < (NbSyncPoints / 2)));
if (bDataIndependentAddr)
{
pbAddrInputZero = new ulong[NbBlockSizeInQW * 3];
const int iInput = (int)NbBlockSizeInQW;
pbAddrInputZero[iInput] = ti.Pass;
pbAddrInputZero[iInput + 1] = ti.Lane;
pbAddrInputZero[iInput + 2] = ti.Slice;
pbAddrInputZero[iInput + 3] = ctx.MemoryBlocks;
pbAddrInputZero[iInput + 4] = ctx.TCost;
pbAddrInputZero[iInput + 5] = (ulong)ctx.Type;
}
ulong uStart = 0;
if ((ti.Pass == 0) && (ti.Slice == 0))
{
uStart = 2;
if (bDataIndependentAddr)
NextAddresses(pbAddrInputZero, pbR, pbTmp);
}
ulong uCur = (ti.Lane * ctx.LaneLength) + (ti.Slice *
ctx.SegmentLength) + uStart;
ulong uPrev = (((uCur % ctx.LaneLength) == 0) ?
(uCur + ctx.LaneLength - 1UL) : (uCur - 1UL));
for (ulong i = uStart; i < ctx.SegmentLength; ++i)
{
if ((uCur % ctx.LaneLength) == 1)
uPrev = uCur - 1UL;
ulong uPseudoRand;
if (bDataIndependentAddr)
{
ulong iMod = i % NbAddressesInBlock;
if (iMod == 0)
NextAddresses(pbAddrInputZero, pbR, pbTmp);
uPseudoRand = pbAddrInputZero[iMod];
}
else 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);
if (pbAddrInputZero != null) MemUtil.ZeroArray<ulong>(pbAddrInputZero);
}
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 void NextAddresses(ulong[] pbAddrInputZero, ulong[] pbR,
ulong[] pbTmp)
{
// pbAddrInputZero contains an address block, an input block and a zero block
const ulong uAddr = 0;
const ulong uInput = NbBlockSizeInQW;
const ulong uZero = NbBlockSizeInQW * 2;
++pbAddrInputZero[uInput + 6];
FillBlock(pbAddrInputZero, uZero, uInput, uAddr, false, pbR, pbTmp);
FillBlock(pbAddrInputZero, uZero, uAddr, uAddr, false, pbR, pbTmp);
}
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,226 @@
/*
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;
namespace KeePassLib.Cryptography.KeyDerivation
{
public enum Argon2Type
{
// The values must be the same as in the Argon2 specification
D = 0,
ID = 2
}
public sealed partial class Argon2Kdf : KdfEngine
{
private static readonly PwUuid g_uuidD = new PwUuid(new byte[] {
0xEF, 0x63, 0x6D, 0xDF, 0x8C, 0x29, 0x44, 0x4B,
0x91, 0xF7, 0xA9, 0xA4, 0x03, 0xE3, 0x0A, 0x0C });
private static readonly PwUuid g_uuidID = new PwUuid(new byte[] {
0x9E, 0x29, 0x8B, 0x19, 0x56, 0xDB, 0x47, 0x73,
0xB2, 0x3D, 0xFC, 0x3E, 0xC6, 0xF0, 0xA1, 0xE6 });
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 = 64 * 1024 * 1024; // 64 MB
internal const uint DefaultParallelism = 2;
private readonly Argon2Type m_t;
public override PwUuid Uuid
{
get { return ((m_t == Argon2Type.D) ? g_uuidD : g_uuidID); }
}
public override string Name
{
get { return ((m_t == Argon2Type.D) ? "Argon2d" : "Argon2id"); }
}
public Argon2Kdf() : this(Argon2Type.D)
{
}
public Argon2Kdf(Argon2Type t)
{
if ((t != Argon2Type.D) && (t != Argon2Type.ID))
throw new NotSupportedException();
m_t = t;
}
public override byte[] GetSeed(KdfParameters p)
{ return p.GetByteArray(ParamSalt); }
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(p.KdfUuid.Equals(this.Uuid));
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;
if (m_t == Argon2Type.ID)
{
pbRet = Argon2Transform(pbMsg, pbSalt, uPar, uMem,
uIt, 32, v, pbSecretKey, pbAssocData);
}
else
{
if (pbSecretKey != null)
{
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbSecretKey");
}
if (pbAssocData != null)
{
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbAssocData");
}
/*
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
32, v, pbSecretKey, pbAssocData);
*/
IntPtr msgPtr = Marshal.AllocHGlobal(pbMsg.Length);
IntPtr saltPtr = Marshal.AllocHGlobal(pbSalt.Length);
IntPtr retPtr = Marshal.AllocHGlobal(32);
Marshal.Copy(pbMsg, 0, msgPtr, pbMsg.Length);
Marshal.Copy(pbSalt, 0, saltPtr, pbSalt.Length);
const UInt32 Argon2_d = 0;
int ret = argon2_hash(
(UInt32)uIt, (UInt32)(uMem / 1024), uPar,
msgPtr, (IntPtr)pbMsg.Length,
saltPtr, (IntPtr)pbSalt.Length,
retPtr, (IntPtr)32,
(IntPtr)0, (IntPtr)0, Argon2_d, v);
if (ret != 0)
{
throw new Exception("argon2_hash failed with " + ret);
}
pbRet = new byte[32];
Marshal.Copy(retPtr, pbRet, 0, 32);
Marshal.FreeHGlobal(msgPtr);
Marshal.FreeHGlobal(saltPtr);
Marshal.FreeHGlobal(retPtr);
}
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;
}
[DllImport("argon2")]
static extern int argon2_hash(
UInt32 t_cost, UInt32 m_cost, UInt32 parallelism,
IntPtr pwd, IntPtr pwdlen,
IntPtr salt, IntPtr saltlen,
IntPtr hash, IntPtr hashlen,
IntPtr encoded, IntPtr encodedlen,
UInt32 type, UInt32 version);
}
}

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 KeePassLib.Cryptography.KeyDerivation
{
public abstract class KdfEngine
{
public abstract PwUuid Uuid
{
get;
}
public abstract string Name
{
get;
}
public abstract byte[] GetSeed(KdfParameters p);
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-2020 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 KeePassLib.Collections;
using KeePassLib.Utility;
namespace KeePassLib.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,97 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2020 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 KeePassLib.Utility;
namespace KeePassLib.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(Argon2Type.D));
g_l.Add(new Argon2Kdf(Argon2Type.ID));
}
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

@@ -0,0 +1,65 @@
/*
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.Text;
using System.Diagnostics;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.PasswordGenerator
{
internal static class CharSetBasedGenerator
{
internal static PwgError Generate(out ProtectedString psOut,
PwProfile pwProfile, CryptoRandomStream crsRandomSource)
{
psOut = ProtectedString.Empty;
if(pwProfile.Length == 0) return PwgError.Success;
PwCharSet pcs = new PwCharSet(pwProfile.CharSet.ToString());
char[] vGenerated = new char[pwProfile.Length];
PwGenerator.PrepareCharSet(pcs, pwProfile);
for(int nIndex = 0; nIndex < (int)pwProfile.Length; ++nIndex)
{
char ch = PwGenerator.GenerateCharacter(pwProfile, pcs,
crsRandomSource);
if(ch == char.MinValue)
{
MemUtil.ZeroArray<char>(vGenerated);
return PwgError.TooFewCharacters;
}
vGenerated[nIndex] = ch;
}
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vGenerated);
psOut = new ProtectedString(true, pbUtf8);
MemUtil.ZeroByteArray(pbUtf8);
MemUtil.ZeroArray<char>(vGenerated);
return PwgError.Success;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
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.Text;
using KeePassLib;
using KeePassLib.Security;
namespace KeePassLib.Cryptography.PasswordGenerator
{
public abstract class CustomPwGenerator
{
/// <summary>
/// Each custom password generation algorithm must have
/// its own unique UUID.
/// </summary>
public abstract PwUuid Uuid { get; }
/// <summary>
/// Displayable name of the password generation algorithm.
/// </summary>
public abstract string Name { get; }
public virtual bool SupportsOptions
{
get { return false; }
}
/// <summary>
/// Password generation function.
/// </summary>
/// <param name="prf">Password generation options chosen
/// by the user. This may be <c>null</c>, if the default
/// options should be used.</param>
/// <param name="crsRandomSource">Source that the algorithm
/// can use to generate random numbers.</param>
/// <returns>Generated password or <c>null</c> in case
/// of failure. If returning <c>null</c>, the caller assumes
/// that an error message has already been shown to the user.</returns>
public abstract ProtectedString Generate(PwProfile prf,
CryptoRandomStream crsRandomSource);
public virtual string GetOptions(string strCurrentOptions)
{
return string.Empty;
}
}
}

View File

@@ -0,0 +1,110 @@
/*
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.Text;
namespace KeePassLib.Cryptography.PasswordGenerator
{
public sealed class CustomPwGeneratorPool : IEnumerable<CustomPwGenerator>
{
private List<CustomPwGenerator> m_vGens = new List<CustomPwGenerator>();
public int Count
{
get { return m_vGens.Count; }
}
public CustomPwGeneratorPool()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vGens.GetEnumerator();
}
public IEnumerator<CustomPwGenerator> GetEnumerator()
{
return m_vGens.GetEnumerator();
}
public void Add(CustomPwGenerator pwg)
{
if(pwg == null) throw new ArgumentNullException("pwg");
PwUuid uuid = pwg.Uuid;
if(uuid == null) throw new ArgumentException();
int nIndex = FindIndex(uuid);
if(nIndex >= 0) m_vGens[nIndex] = pwg; // Replace
else m_vGens.Add(pwg);
}
public CustomPwGenerator Find(PwUuid uuid)
{
if(uuid == null) throw new ArgumentNullException("uuid");
foreach(CustomPwGenerator pwg in m_vGens)
{
if(uuid.Equals(pwg.Uuid)) return pwg;
}
return null;
}
public CustomPwGenerator Find(string strName)
{
if(strName == null) throw new ArgumentNullException("strName");
foreach(CustomPwGenerator pwg in m_vGens)
{
if(pwg.Name == strName) return pwg;
}
return null;
}
private int FindIndex(PwUuid uuid)
{
if(uuid == null) throw new ArgumentNullException("uuid");
for(int i = 0; i < m_vGens.Count; ++i)
{
if(uuid.Equals(m_vGens[i].Uuid)) return i;
}
return -1;
}
public bool Remove(PwUuid uuid)
{
if(uuid == null) throw new ArgumentNullException("uuid");
int nIndex = FindIndex(uuid);
if(nIndex < 0) return false;
m_vGens.RemoveAt(nIndex);
return true;
}
}
}

View File

@@ -0,0 +1,173 @@
/*
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.Text;
using System.Diagnostics;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.PasswordGenerator
{
internal static class PatternBasedGenerator
{
internal static PwgError Generate(out ProtectedString psOut,
PwProfile pwProfile, CryptoRandomStream crsRandomSource)
{
psOut = ProtectedString.Empty;
LinkedList<char> vGenerated = new LinkedList<char>();
PwCharSet pcsCurrent = new PwCharSet();
PwCharSet pcsCustom = new PwCharSet();
PwCharSet pcsUsed = new PwCharSet();
bool bInCharSetDef = false;
string strPattern = ExpandPattern(pwProfile.Pattern);
if(strPattern.Length == 0) return PwgError.Success;
CharStream csStream = new CharStream(strPattern);
char ch = csStream.ReadChar();
while(ch != char.MinValue)
{
pcsCurrent.Clear();
bool bGenerateChar = false;
if(ch == '\\')
{
ch = csStream.ReadChar();
if(ch == char.MinValue) // Backslash at the end
{
vGenerated.AddLast('\\');
break;
}
if(bInCharSetDef) pcsCustom.Add(ch);
else
{
vGenerated.AddLast(ch);
pcsUsed.Add(ch);
}
}
else if(ch == '[')
{
pcsCustom.Clear();
bInCharSetDef = true;
}
else if(ch == ']')
{
pcsCurrent.Add(pcsCustom.ToString());
bInCharSetDef = false;
bGenerateChar = true;
}
else if(bInCharSetDef)
{
if(pcsCustom.AddCharSet(ch) == false)
pcsCustom.Add(ch);
}
else if(pcsCurrent.AddCharSet(ch) == false)
{
vGenerated.AddLast(ch);
pcsUsed.Add(ch);
}
else bGenerateChar = true;
if(bGenerateChar)
{
PwGenerator.PrepareCharSet(pcsCurrent, pwProfile);
if(pwProfile.NoRepeatingCharacters)
pcsCurrent.Remove(pcsUsed.ToString());
char chGen = PwGenerator.GenerateCharacter(pwProfile,
pcsCurrent, crsRandomSource);
if(chGen == char.MinValue) return PwgError.TooFewCharacters;
vGenerated.AddLast(chGen);
pcsUsed.Add(chGen);
}
ch = csStream.ReadChar();
}
if(vGenerated.Count == 0) return PwgError.Success;
char[] vArray = new char[vGenerated.Count];
vGenerated.CopyTo(vArray, 0);
if(pwProfile.PatternPermutePassword)
PwGenerator.ShufflePassword(vArray, crsRandomSource);
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vArray);
psOut = new ProtectedString(true, pbUtf8);
MemUtil.ZeroByteArray(pbUtf8);
MemUtil.ZeroArray<char>(vArray);
vGenerated.Clear();
return PwgError.Success;
}
private static string ExpandPattern(string strPattern)
{
Debug.Assert(strPattern != null); if(strPattern == null) return string.Empty;
string str = strPattern;
while(true)
{
int nOpen = FindFirstUnescapedChar(str, '{');
int nClose = FindFirstUnescapedChar(str, '}');
if((nOpen >= 0) && (nOpen < nClose))
{
string strCount = str.Substring(nOpen + 1, nClose - nOpen - 1);
str = str.Remove(nOpen, nClose - nOpen + 1);
uint uRepeat;
if(StrUtil.TryParseUInt(strCount, out uRepeat) && (nOpen >= 1))
{
if(uRepeat == 0)
str = str.Remove(nOpen - 1, 1);
else
str = str.Insert(nOpen, new string(str[nOpen - 1], (int)uRepeat - 1));
}
}
else break;
}
return str;
}
private static int FindFirstUnescapedChar(string str, char ch)
{
for(int i = 0; i < str.Length; ++i)
{
char chCur = str[i];
if(chCur == '\\') ++i; // Next is escaped, skip it
else if(chCur == ch) return i;
}
return -1;
}
}
}

View File

@@ -0,0 +1,351 @@
/*
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.Text;
using System.Diagnostics;
namespace KeePassLib.Cryptography.PasswordGenerator
{
public sealed class PwCharSet
{
public const string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public const string LowerCase = "abcdefghijklmnopqrstuvwxyz";
public const string Digits = "0123456789";
public const string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ";
public const string LowerConsonants = "bcdfghjklmnpqrstvwxyz";
public const string UpperVowels = "AEIOU";
public const string LowerVowels = "aeiou";
public const string Punctuation = @",.;:";
public const string Brackets = @"[]{}()<>";
public const string PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
public const string UpperHex = "0123456789ABCDEF";
public const string LowerHex = "0123456789abcdef";
public const string Invalid = "\t\r\n";
public const string LookAlike = @"O0l1I|";
internal const string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits;
private const int CharTabSize = (0x10000 / 8);
private List<char> m_vChars = new List<char>();
private byte[] m_vTab = new byte[CharTabSize];
private static string m_strHighAnsi = null;
public static string HighAnsiChars
{
get
{
if(m_strHighAnsi == null) { new PwCharSet(); } // Create string
Debug.Assert(m_strHighAnsi != null);
return m_strHighAnsi;
}
}
private static string m_strSpecial = null;
public static string SpecialChars
{
get
{
if(m_strSpecial == null) { new PwCharSet(); } // Create string
Debug.Assert(m_strSpecial != null);
return m_strSpecial;
}
}
/// <summary>
/// Create a new, empty character set collection object.
/// </summary>
public PwCharSet()
{
Initialize(true);
}
public PwCharSet(string strCharSet)
{
Initialize(true);
Add(strCharSet);
}
private PwCharSet(bool bFullInitialize)
{
Initialize(bFullInitialize);
}
private void Initialize(bool bFullInitialize)
{
Clear();
if(!bFullInitialize) return;
if(m_strHighAnsi == null)
{
StringBuilder sbHighAnsi = new StringBuilder();
// [U+0080, U+009F] are C1 control characters,
// U+00A0 is non-breaking space
for(char ch = '\u00A1'; ch <= '\u00AC'; ++ch)
sbHighAnsi.Append(ch);
// U+00AD is soft hyphen (format character)
for(char ch = '\u00AE'; ch < '\u00FF'; ++ch)
sbHighAnsi.Append(ch);
sbHighAnsi.Append('\u00FF');
m_strHighAnsi = sbHighAnsi.ToString();
}
if(m_strSpecial == null)
{
PwCharSet pcs = new PwCharSet(false);
pcs.AddRange('!', '/');
pcs.AddRange(':', '@');
pcs.AddRange('[', '`');
pcs.Add(@"|~");
pcs.Remove(@"-_ ");
pcs.Remove(PwCharSet.Brackets);
m_strSpecial = pcs.ToString();
}
}
/// <summary>
/// Number of characters in this set.
/// </summary>
public uint Size
{
get { return (uint)m_vChars.Count; }
}
/// <summary>
/// Get a character of the set using an index.
/// </summary>
/// <param name="uPos">Index of the character to get.</param>
/// <returns>Character at the specified position. If the index is invalid,
/// an <c>ArgumentOutOfRangeException</c> is thrown.</returns>
public char this[uint uPos]
{
get
{
if(uPos >= (uint)m_vChars.Count)
throw new ArgumentOutOfRangeException("uPos");
return m_vChars[(int)uPos];
}
}
/// <summary>
/// Remove all characters from this set.
/// </summary>
public void Clear()
{
m_vChars.Clear();
Array.Clear(m_vTab, 0, m_vTab.Length);
}
public bool Contains(char ch)
{
return (((m_vTab[ch / 8] >> (ch % 8)) & 1) != char.MinValue);
}
public bool Contains(string strCharacters)
{
Debug.Assert(strCharacters != null);
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
foreach(char ch in strCharacters)
{
if(!Contains(ch)) return false;
}
return true;
}
/// <summary>
/// Add characters to the set.
/// </summary>
/// <param name="ch">Character to add.</param>
public void Add(char ch)
{
if(ch == char.MinValue) { Debug.Assert(false); return; }
if(!Contains(ch))
{
m_vChars.Add(ch);
m_vTab[ch / 8] |= (byte)(1 << (ch % 8));
}
}
/// <summary>
/// Add characters to the set.
/// </summary>
/// <param name="strCharSet">String containing characters to add.</param>
public void Add(string strCharSet)
{
Debug.Assert(strCharSet != null);
if(strCharSet == null) throw new ArgumentNullException("strCharSet");
m_vChars.Capacity = m_vChars.Count + strCharSet.Length;
foreach(char ch in strCharSet)
Add(ch);
}
public void Add(string strCharSet1, string strCharSet2)
{
Add(strCharSet1);
Add(strCharSet2);
}
public void Add(string strCharSet1, string strCharSet2, string strCharSet3)
{
Add(strCharSet1);
Add(strCharSet2);
Add(strCharSet3);
}
public void AddRange(char chMin, char chMax)
{
m_vChars.Capacity = m_vChars.Count + (chMax - chMin) + 1;
for(char ch = chMin; ch < chMax; ++ch)
Add(ch);
Add(chMax);
}
public bool AddCharSet(char chCharSetIdentifier)
{
bool bResult = true;
switch(chCharSetIdentifier)
{
case 'a': Add(PwCharSet.LowerCase, PwCharSet.Digits); break;
case 'A': Add(PwCharSet.LowerCase, PwCharSet.UpperCase,
PwCharSet.Digits); break;
case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break;
case 'c': Add(PwCharSet.LowerConsonants); break;
case 'C': Add(PwCharSet.LowerConsonants,
PwCharSet.UpperConsonants); break;
case 'z': Add(PwCharSet.UpperConsonants); break;
case 'd': Add(PwCharSet.Digits); break; // Digit
case 'h': Add(PwCharSet.LowerHex); break;
case 'H': Add(PwCharSet.UpperHex); break;
case 'l': Add(PwCharSet.LowerCase); break;
case 'L': Add(PwCharSet.LowerCase, PwCharSet.UpperCase); break;
case 'u': Add(PwCharSet.UpperCase); break;
case 'p': Add(PwCharSet.Punctuation); break;
case 'b': Add(PwCharSet.Brackets); break;
case 's': Add(PwCharSet.PrintableAsciiSpecial); break;
case 'S': Add(PwCharSet.UpperCase, PwCharSet.LowerCase);
Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break;
case 'v': Add(PwCharSet.LowerVowels); break;
case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break;
case 'Z': Add(PwCharSet.UpperVowels); break;
case 'x': Add(m_strHighAnsi); break;
default: bResult = false; break;
}
return bResult;
}
public bool Remove(char ch)
{
m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8)));
return m_vChars.Remove(ch);
}
public bool Remove(string strCharacters)
{
Debug.Assert(strCharacters != null);
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
bool bResult = true;
foreach(char ch in strCharacters)
{
if(!Remove(ch)) bResult = false;
}
return bResult;
}
public bool RemoveIfAllExist(string strCharacters)
{
Debug.Assert(strCharacters != null);
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
if(!Contains(strCharacters))
return false;
return Remove(strCharacters);
}
/// <summary>
/// Convert the character set to a string containing all its characters.
/// </summary>
/// <returns>String containing all character set characters.</returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach(char ch in m_vChars)
sb.Append(ch);
return sb.ToString();
}
public string PackAndRemoveCharRanges()
{
StringBuilder sb = new StringBuilder();
sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_');
sb.Append(RemoveIfAllExist(m_strSpecial) ? 'S' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_');
sb.Append(RemoveIfAllExist(@"-") ? 'm' : '_');
sb.Append(RemoveIfAllExist(@"_") ? 'u' : '_');
sb.Append(RemoveIfAllExist(@" ") ? 's' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_');
sb.Append(RemoveIfAllExist(m_strHighAnsi) ? 'H' : '_');
return sb.ToString();
}
public void UnpackCharRanges(string strRanges)
{
if(strRanges == null) { Debug.Assert(false); return; }
if(strRanges.Length < 10) { Debug.Assert(false); return; }
if(strRanges[0] != '_') Add(PwCharSet.UpperCase);
if(strRanges[1] != '_') Add(PwCharSet.LowerCase);
if(strRanges[2] != '_') Add(PwCharSet.Digits);
if(strRanges[3] != '_') Add(m_strSpecial);
if(strRanges[4] != '_') Add(PwCharSet.Punctuation);
if(strRanges[5] != '_') Add('-');
if(strRanges[6] != '_') Add('_');
if(strRanges[7] != '_') Add(' ');
if(strRanges[8] != '_') Add(PwCharSet.Brackets);
if(strRanges[9] != '_') Add(m_strHighAnsi);
}
}
}

View File

@@ -0,0 +1,152 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2016 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.Security.Cryptography;
using System.Text;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.PasswordGenerator
{
public enum PwgError
{
Success = 0,
Unknown = 1,
TooFewCharacters = 2,
UnknownAlgorithm = 3
}
/// <summary>
/// Utility functions for generating random passwords.
/// </summary>
public static class PwGenerator
{
public static PwgError Generate(out ProtectedString psOut,
PwProfile pwProfile, byte[] pbUserEntropy,
CustomPwGeneratorPool pwAlgorithmPool)
{
Debug.Assert(pwProfile != null);
if (pwProfile == null) throw new ArgumentNullException("pwProfile");
CryptoRandomStream crs = CreateCryptoStream(pbUserEntropy);
PwgError e = PwgError.Unknown;
if (pwProfile.GeneratorType == PasswordGeneratorType.CharSet)
e = CharSetBasedGenerator.Generate(out psOut, pwProfile, crs);
else if (pwProfile.GeneratorType == PasswordGeneratorType.Pattern)
e = PatternBasedGenerator.Generate(out psOut, pwProfile, crs);
else if (pwProfile.GeneratorType == PasswordGeneratorType.Custom)
e = GenerateCustom(out psOut, pwProfile, crs, pwAlgorithmPool);
else { Debug.Assert(false); psOut = ProtectedString.Empty; }
return e;
}
private static CryptoRandomStream CreateCryptoStream(byte[] pbAdditionalEntropy)
{
byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(128);
// Mix in additional entropy
Debug.Assert(pbKey.Length >= 64);
if ((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0))
{
using (SHA512Managed h = new SHA512Managed())
{
byte[] pbHash = h.ComputeHash(pbAdditionalEntropy);
MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length);
}
}
return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
}
internal static char GenerateCharacter(PwProfile pwProfile,
PwCharSet pwCharSet, CryptoRandomStream crsRandomSource)
{
if (pwCharSet.Size == 0) return char.MinValue;
ulong uIndex = crsRandomSource.GetRandomUInt64();
uIndex %= (ulong)pwCharSet.Size;
char ch = pwCharSet[(uint)uIndex];
if (pwProfile.NoRepeatingCharacters)
pwCharSet.Remove(ch);
return ch;
}
internal static void PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
{
pwCharSet.Remove(PwCharSet.Invalid);
if (pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike);
if (pwProfile.ExcludeCharacters.Length > 0)
pwCharSet.Remove(pwProfile.ExcludeCharacters);
}
internal static void ShufflePassword(char[] pPassword,
CryptoRandomStream crsRandomSource)
{
Debug.Assert(pPassword != null); if (pPassword == null) return;
Debug.Assert(crsRandomSource != null); if (crsRandomSource == null) return;
if (pPassword.Length <= 1) return; // Nothing to shuffle
for (int nSelect = 0; nSelect < pPassword.Length; ++nSelect)
{
ulong uRandomIndex = crsRandomSource.GetRandomUInt64();
uRandomIndex %= (ulong)(pPassword.Length - nSelect);
char chTemp = pPassword[nSelect];
pPassword[nSelect] = pPassword[nSelect + (int)uRandomIndex];
pPassword[nSelect + (int)uRandomIndex] = chTemp;
}
}
private static PwgError GenerateCustom(out ProtectedString psOut,
PwProfile pwProfile, CryptoRandomStream crs,
CustomPwGeneratorPool pwAlgorithmPool)
{
psOut = ProtectedString.Empty;
Debug.Assert(pwProfile.GeneratorType == PasswordGeneratorType.Custom);
if (pwAlgorithmPool == null) return PwgError.UnknownAlgorithm;
string strID = pwProfile.CustomAlgorithmUuid;
if (string.IsNullOrEmpty(strID)) { Debug.Assert(false); return PwgError.UnknownAlgorithm; }
byte[] pbUuid = Convert.FromBase64String(strID);
PwUuid uuid = new PwUuid(pbUuid);
CustomPwGenerator pwg = pwAlgorithmPool.Find(uuid);
if (pwg == null) { Debug.Assert(false); return PwgError.UnknownAlgorithm; }
ProtectedString pwd = pwg.Generate(pwProfile.CloneDeep(), crs);
if (pwd == null) return PwgError.Unknown;
psOut = pwd;
return PwgError.Success;
}
}
}

View File

@@ -0,0 +1,278 @@
/*
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.Text;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Diagnostics;
using KeePassLib.Interfaces;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.PasswordGenerator
{
/// <summary>
/// Type of the password generator. Different types like generators
/// based on given patterns, based on character sets, etc. are
/// available.
/// </summary>
public enum PasswordGeneratorType
{
/// <summary>
/// Generator based on character spaces/sets, i.e. groups
/// of characters like lower-case, upper-case or numeric characters.
/// </summary>
CharSet = 0,
/// <summary>
/// Password generation based on a pattern. The user has provided
/// a pattern, which describes how the generated password has to
/// look like.
/// </summary>
Pattern = 1,
Custom = 2
}
public sealed class PwProfile : IDeepCloneable<PwProfile>
{
private string m_strName = string.Empty;
[DefaultValue("")]
public string Name
{
get { return m_strName; }
set { m_strName = value; }
}
private PasswordGeneratorType m_type = PasswordGeneratorType.CharSet;
public PasswordGeneratorType GeneratorType
{
get { return m_type; }
set { m_type = value; }
}
private bool m_bUserEntropy = false;
[DefaultValue(false)]
public bool CollectUserEntropy
{
get { return m_bUserEntropy; }
set { m_bUserEntropy = value; }
}
private uint m_uLength = 20;
public uint Length
{
get { return m_uLength; }
set { m_uLength = value; }
}
private PwCharSet m_pwCharSet = new PwCharSet(PwCharSet.UpperCase +
PwCharSet.LowerCase + PwCharSet.Digits);
[XmlIgnore]
public PwCharSet CharSet
{
get { return m_pwCharSet; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_pwCharSet = value;
}
}
private string m_strCharSetRanges = string.Empty;
[DefaultValue("")]
public string CharSetRanges
{
get { this.UpdateCharSet(true); return m_strCharSetRanges; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strCharSetRanges = value;
this.UpdateCharSet(false);
}
}
private string m_strCharSetAdditional = string.Empty;
[DefaultValue("")]
public string CharSetAdditional
{
get { this.UpdateCharSet(true); return m_strCharSetAdditional; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strCharSetAdditional = value;
this.UpdateCharSet(false);
}
}
private string m_strPattern = string.Empty;
[DefaultValue("")]
public string Pattern
{
get { return m_strPattern; }
set { m_strPattern = value; }
}
private bool m_bPatternPermute = false;
[DefaultValue(false)]
public bool PatternPermutePassword
{
get { return m_bPatternPermute; }
set { m_bPatternPermute = value; }
}
private bool m_bNoLookAlike = false;
[DefaultValue(false)]
public bool ExcludeLookAlike
{
get { return m_bNoLookAlike; }
set { m_bNoLookAlike = value; }
}
private bool m_bNoRepeat = false;
[DefaultValue(false)]
public bool NoRepeatingCharacters
{
get { return m_bNoRepeat; }
set { m_bNoRepeat = value; }
}
private string m_strExclude = string.Empty;
[DefaultValue("")]
public string ExcludeCharacters
{
get { return m_strExclude; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strExclude = value;
}
}
private string m_strCustomID = string.Empty;
[DefaultValue("")]
public string CustomAlgorithmUuid
{
get { return m_strCustomID; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strCustomID = value;
}
}
private string m_strCustomOpt = string.Empty;
[DefaultValue("")]
public string CustomAlgorithmOptions
{
get { return m_strCustomOpt; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strCustomOpt = value;
}
}
public PwProfile()
{
}
public PwProfile CloneDeep()
{
PwProfile p = new PwProfile();
p.m_strName = m_strName;
p.m_type = m_type;
p.m_bUserEntropy = m_bUserEntropy;
p.m_uLength = m_uLength;
p.m_pwCharSet = new PwCharSet(m_pwCharSet.ToString());
p.m_strCharSetRanges = m_strCharSetRanges;
p.m_strCharSetAdditional = m_strCharSetAdditional;
p.m_strPattern = m_strPattern;
p.m_bPatternPermute = m_bPatternPermute;
p.m_bNoLookAlike = m_bNoLookAlike;
p.m_bNoRepeat = m_bNoRepeat;
p.m_strExclude = m_strExclude;
p.m_strCustomID = m_strCustomID;
p.m_strCustomOpt = m_strCustomOpt;
return p;
}
private void UpdateCharSet(bool bSetXml)
{
if(bSetXml)
{
PwCharSet pcs = new PwCharSet(m_pwCharSet.ToString());
m_strCharSetRanges = pcs.PackAndRemoveCharRanges();
m_strCharSetAdditional = pcs.ToString();
}
else
{
PwCharSet pcs = new PwCharSet(m_strCharSetAdditional);
pcs.UnpackCharRanges(m_strCharSetRanges);
m_pwCharSet = pcs;
}
}
public static PwProfile DeriveFromPassword(ProtectedString psPassword)
{
PwProfile pp = new PwProfile();
Debug.Assert(psPassword != null); if(psPassword == null) return pp;
byte[] pbUtf8 = psPassword.ReadUtf8();
char[] vChars = StrUtil.Utf8.GetChars(pbUtf8);
pp.GeneratorType = PasswordGeneratorType.CharSet;
pp.Length = (uint)vChars.Length;
PwCharSet pcs = pp.CharSet;
pcs.Clear();
foreach(char ch in vChars)
{
if((ch >= 'A') && (ch <= 'Z')) pcs.Add(PwCharSet.UpperCase);
else if((ch >= 'a') && (ch <= 'z')) pcs.Add(PwCharSet.LowerCase);
else if((ch >= '0') && (ch <= '9')) pcs.Add(PwCharSet.Digits);
else if(PwCharSet.SpecialChars.IndexOf(ch) >= 0)
pcs.Add(PwCharSet.SpecialChars);
else if(ch == ' ') pcs.Add(' ');
else if(ch == '-') pcs.Add('-');
else if(ch == '_') pcs.Add('_');
else if(PwCharSet.Brackets.IndexOf(ch) >= 0)
pcs.Add(PwCharSet.Brackets);
else if(PwCharSet.HighAnsiChars.IndexOf(ch) >= 0)
pcs.Add(PwCharSet.HighAnsiChars);
else pcs.Add(ch);
}
MemUtil.ZeroArray<char>(vChars);
MemUtil.ZeroByteArray(pbUtf8);
return pp;
}
public bool HasSecurityReducingOption()
{
return (m_bNoLookAlike || m_bNoRepeat || (m_strExclude.Length > 0));
}
}
}

View File

@@ -0,0 +1,134 @@
/*
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.Text;
using System.Diagnostics;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
public static class PopularPasswords
{
private static Dictionary<int, Dictionary<string, bool>> m_dicts =
new Dictionary<int, Dictionary<string, bool>>();
internal static int MaxLength
{
get
{
Debug.Assert(m_dicts.Count > 0); // Should be initialized
int iMaxLen = 0;
foreach(int iLen in m_dicts.Keys)
{
if(iLen > iMaxLen) iMaxLen = iLen;
}
return iMaxLen;
}
}
internal static bool ContainsLength(int nLength)
{
Dictionary<string, bool> dDummy;
return m_dicts.TryGetValue(nLength, out dDummy);
}
public static bool IsPopularPassword(char[] vPassword)
{
ulong uDummy;
return IsPopularPassword(vPassword, out uDummy);
}
public static bool IsPopularPassword(char[] vPassword, out ulong uDictSize)
{
if(vPassword == null) throw new ArgumentNullException("vPassword");
if(vPassword.Length == 0) { uDictSize = 0; return false; }
string str = new string(vPassword);
try { return IsPopularPasswordPriv(str, out uDictSize); }
catch(Exception) { Debug.Assert(false); }
uDictSize = 0;
return false;
}
private static bool IsPopularPasswordPriv(string str, out ulong uDictSize)
{
Debug.Assert(m_dicts.Count > 0); // Should be initialized with data
Dictionary<string, bool> d;
if(!m_dicts.TryGetValue(str.Length, out d))
{
uDictSize = 0;
return false;
}
uDictSize = (ulong)d.Count;
return d.ContainsKey(str);
}
public static void Add(byte[] pbData, bool bGZipped)
{
try
{
if(bGZipped)
pbData = MemUtil.Decompress(pbData);
string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length);
if(string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; }
if(!char.IsWhiteSpace(strData[strData.Length - 1]))
strData += "\n";
StringBuilder sb = new StringBuilder();
for(int i = 0; i < strData.Length; ++i)
{
char ch = strData[i];
if(char.IsWhiteSpace(ch))
{
int cc = sb.Length;
if(cc > 0)
{
string strWord = sb.ToString();
Debug.Assert(strWord.Length == cc);
Dictionary<string, bool> d;
if(!m_dicts.TryGetValue(cc, out d))
{
d = new Dictionary<string, bool>();
m_dicts[cc] = d;
}
d[strWord] = true;
sb.Remove(0, cc);
}
}
else sb.Append(char.ToLower(ch));
}
}
catch(Exception) { Debug.Assert(false); }
}
}
}

View File

@@ -0,0 +1,768 @@
/*
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.Text;
using System.Diagnostics;
using KeePassLib.Cryptography.PasswordGenerator;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
/// <summary>
/// A class that offers static functions to estimate the quality of
/// passwords.
/// </summary>
public static class QualityEstimation
{
private static class PatternID
{
public const char LowerAlpha = 'L';
public const char UpperAlpha = 'U';
public const char Digit = 'D';
public const char Special = 'S';
public const char High = 'H';
public const char Other = 'X';
public const char Dictionary = 'W';
public const char Repetition = 'R';
public const char Number = 'N';
public const char DiffSeq = 'C';
public const string All = "LUDSHXWRNC";
}
// private static class CharDistrib
// {
// public static readonly ulong[] LowerAlpha = new ulong[26] {
// 884, 211, 262, 249, 722, 98, 172, 234, 556, 124, 201, 447, 321,
// 483, 518, 167, 18, 458, 416, 344, 231, 105, 80, 48, 238, 76
// };
// public static readonly ulong[] UpperAlpha = new ulong[26] {
// 605, 188, 209, 200, 460, 81, 130, 163, 357, 122, 144, 332, 260,
// 317, 330, 132, 18, 320, 315, 250, 137, 76, 60, 36, 161, 54
// };
// public static readonly ulong[] Digit = new ulong[10] {
// 574, 673, 524, 377, 339, 336, 312, 310, 357, 386
// };
// }
private sealed class QeCharType
{
private readonly char m_chTypeID;
public char TypeID { get { return m_chTypeID; } }
private readonly string m_strAlph;
public string Alphabet { get { return m_strAlph; } }
private readonly int m_nChars;
public int CharCount { get { return m_nChars; } }
private readonly char m_chFirst;
private readonly char m_chLast;
private readonly double m_dblCharSize;
public double CharSize { get { return m_dblCharSize; } }
public QeCharType(char chTypeID, string strAlphabet, bool bIsConsecutive)
{
if(strAlphabet == null) throw new ArgumentNullException();
if(strAlphabet.Length == 0) throw new ArgumentException();
m_chTypeID = chTypeID;
m_strAlph = strAlphabet;
m_nChars = m_strAlph.Length;
m_chFirst = (bIsConsecutive ? m_strAlph[0] : char.MinValue);
m_chLast = (bIsConsecutive ? m_strAlph[m_nChars - 1] : char.MinValue);
m_dblCharSize = Log2(m_nChars);
Debug.Assert(((int)(m_chLast - m_chFirst) == (m_nChars - 1)) ||
!bIsConsecutive);
}
public QeCharType(char chTypeID, int nChars) // Catch-none set
{
if(nChars <= 0) throw new ArgumentOutOfRangeException();
m_chTypeID = chTypeID;
m_strAlph = string.Empty;
m_nChars = nChars;
m_chFirst = char.MinValue;
m_chLast = char.MinValue;
m_dblCharSize = Log2(m_nChars);
}
public bool Contains(char ch)
{
if(m_chLast != char.MinValue)
return ((ch >= m_chFirst) && (ch <= m_chLast));
Debug.Assert(m_strAlph.Length > 0); // Don't call for catch-none set
return (m_strAlph.IndexOf(ch) >= 0);
}
}
private sealed class EntropyEncoder
{
private readonly string m_strAlph;
private Dictionary<char, ulong> m_dHisto = new Dictionary<char, ulong>();
private readonly ulong m_uBaseWeight;
private readonly ulong m_uCharWeight;
private readonly ulong m_uOccExclThreshold;
public EntropyEncoder(string strAlphabet, ulong uBaseWeight,
ulong uCharWeight, ulong uOccExclThreshold)
{
if(strAlphabet == null) throw new ArgumentNullException();
if(strAlphabet.Length == 0) throw new ArgumentException();
m_strAlph = strAlphabet;
m_uBaseWeight = uBaseWeight;
m_uCharWeight = uCharWeight;
m_uOccExclThreshold = uOccExclThreshold;
#if DEBUG
Dictionary<char, bool> d = new Dictionary<char, bool>();
foreach(char ch in m_strAlph) { d[ch] = true; }
Debug.Assert(d.Count == m_strAlph.Length); // No duplicates
#endif
}
public void Reset()
{
m_dHisto.Clear();
}
public void Write(char ch)
{
Debug.Assert(m_strAlph.IndexOf(ch) >= 0);
ulong uOcc;
m_dHisto.TryGetValue(ch, out uOcc);
Debug.Assert(m_dHisto.ContainsKey(ch) || (uOcc == 0));
m_dHisto[ch] = uOcc + 1;
}
public double GetOutputSize()
{
ulong uTotalWeight = m_uBaseWeight * (ulong)m_strAlph.Length;
foreach(ulong u in m_dHisto.Values)
{
Debug.Assert(u >= 1);
if(u > m_uOccExclThreshold)
uTotalWeight += (u - m_uOccExclThreshold) * m_uCharWeight;
}
double dSize = 0.0, dTotalWeight = (double)uTotalWeight;
foreach(ulong u in m_dHisto.Values)
{
ulong uWeight = m_uBaseWeight;
if(u > m_uOccExclThreshold)
uWeight += (u - m_uOccExclThreshold) * m_uCharWeight;
dSize -= (double)u * Log2((double)uWeight / dTotalWeight);
}
return dSize;
}
}
private sealed class MultiEntropyEncoder
{
private Dictionary<char, EntropyEncoder> m_dEncs =
new Dictionary<char, EntropyEncoder>();
public MultiEntropyEncoder()
{
}
public void AddEncoder(char chTypeID, EntropyEncoder ec)
{
if(ec == null) { Debug.Assert(false); return; }
Debug.Assert(!m_dEncs.ContainsKey(chTypeID));
m_dEncs[chTypeID] = ec;
}
public void Reset()
{
foreach(EntropyEncoder ec in m_dEncs.Values) { ec.Reset(); }
}
public bool Write(char chTypeID, char chData)
{
EntropyEncoder ec;
if(!m_dEncs.TryGetValue(chTypeID, out ec))
return false;
ec.Write(chData);
return true;
}
public double GetOutputSize()
{
double d = 0.0;
foreach(EntropyEncoder ec in m_dEncs.Values)
{
d += ec.GetOutputSize();
}
return d;
}
}
private sealed class QePatternInstance
{
private readonly int m_iPos;
public int Position { get { return m_iPos; } }
private readonly int m_nLen;
public int Length { get { return m_nLen; } }
private readonly char m_chPatternID;
public char PatternID { get { return m_chPatternID; } }
private readonly double m_dblCost;
public double Cost { get { return m_dblCost; } }
private readonly QeCharType m_ctSingle;
public QeCharType SingleCharType { get { return m_ctSingle; } }
public QePatternInstance(int iPosition, int nLength, char chPatternID,
double dblCost)
{
m_iPos = iPosition;
m_nLen = nLength;
m_chPatternID = chPatternID;
m_dblCost = dblCost;
m_ctSingle = null;
}
public QePatternInstance(int iPosition, int nLength, QeCharType ctSingle)
{
m_iPos = iPosition;
m_nLen = nLength;
m_chPatternID = ctSingle.TypeID;
m_dblCost = ctSingle.CharSize;
m_ctSingle = ctSingle;
}
}
private sealed class QePathState
{
public readonly int Position;
public readonly List<QePatternInstance> Path;
public QePathState(int iPosition, List<QePatternInstance> lPath)
{
this.Position = iPosition;
this.Path = lPath;
}
}
private static object m_objSyncInit = new object();
private static List<QeCharType> m_lCharTypes = null;
private static void EnsureInitialized()
{
lock(m_objSyncInit)
{
if(m_lCharTypes == null)
{
string strSpecial = PwCharSet.PrintableAsciiSpecial;
if(strSpecial.IndexOf(' ') >= 0) { Debug.Assert(false); }
else strSpecial = strSpecial + " ";
int nSp = strSpecial.Length;
int nHi = PwCharSet.HighAnsiChars.Length;
m_lCharTypes = new List<QeCharType>();
m_lCharTypes.Add(new QeCharType(PatternID.LowerAlpha,
PwCharSet.LowerCase, true));
m_lCharTypes.Add(new QeCharType(PatternID.UpperAlpha,
PwCharSet.UpperCase, true));
m_lCharTypes.Add(new QeCharType(PatternID.Digit,
PwCharSet.Digits, true));
m_lCharTypes.Add(new QeCharType(PatternID.Special,
strSpecial, false));
m_lCharTypes.Add(new QeCharType(PatternID.High,
PwCharSet.HighAnsiChars, false));
m_lCharTypes.Add(new QeCharType(PatternID.Other,
0x10000 - (2 * 26) - 10 - nSp - nHi));
}
}
}
/// <summary>
/// Estimate the quality of a password.
/// </summary>
/// <param name="vPasswordChars">Password to check.</param>
/// <returns>Estimated bit-strength of the password.</returns>
public static uint EstimatePasswordBits(char[] vPasswordChars)
{
if(vPasswordChars == null) { Debug.Assert(false); return 0; }
if(vPasswordChars.Length == 0) return 0;
EnsureInitialized();
int n = vPasswordChars.Length;
List<QePatternInstance>[] vPatterns = new List<QePatternInstance>[n];
for(int i = 0; i < n; ++i)
{
vPatterns[i] = new List<QePatternInstance>();
QePatternInstance piChar = new QePatternInstance(i, 1,
GetCharType(vPasswordChars[i]));
vPatterns[i].Add(piChar);
}
FindRepetitions(vPasswordChars, vPatterns);
FindNumbers(vPasswordChars, vPatterns);
FindDiffSeqs(vPasswordChars, vPatterns);
FindPopularPasswords(vPasswordChars, vPatterns);
// Encoders must not be static, because the entropy estimation
// may run concurrently in multiple threads and the encoders are
// not read-only
EntropyEncoder ecPattern = new EntropyEncoder(PatternID.All, 0, 1, 0);
MultiEntropyEncoder mcData = new MultiEntropyEncoder();
for(int i = 0; i < (m_lCharTypes.Count - 1); ++i)
{
// Let m be the alphabet size. In order to ensure that two same
// characters cost at least as much as a single character, for
// the probability p and weight w of the character it must hold:
// -log(1/m) >= -2*log(p)
// <=> log(1/m) <= log(p^2) <=> 1/m <= p^2 <=> p >= sqrt(1/m);
// sqrt(1/m) = (1+w)/(m+w)
// <=> m+w = (1+w)*sqrt(m) <=> m+w = sqrt(m) + w*sqrt(m)
// <=> w*(1-sqrt(m)) = sqrt(m) - m <=> w = (sqrt(m)-m)/(1-sqrt(m))
// <=> w = (sqrt(m)-m)*(1+sqrt(m))/(1-m)
// <=> w = (sqrt(m)-m+m-m*sqrt(m))/(1-m) <=> w = sqrt(m)
ulong uw = (ulong)Math.Sqrt((double)m_lCharTypes[i].CharCount);
mcData.AddEncoder(m_lCharTypes[i].TypeID, new EntropyEncoder(
m_lCharTypes[i].Alphabet, 1, uw, 1));
}
double dblMinCost = (double)int.MaxValue;
int tStart = Environment.TickCount;
Stack<QePathState> sRec = new Stack<QePathState>();
sRec.Push(new QePathState(0, new List<QePatternInstance>()));
while(sRec.Count > 0)
{
int tDiff = Environment.TickCount - tStart;
if(tDiff > 500) break;
QePathState s = sRec.Pop();
if(s.Position >= n)
{
Debug.Assert(s.Position == n);
double dblCost = ComputePathCost(s.Path, vPasswordChars,
ecPattern, mcData);
if(dblCost < dblMinCost) dblMinCost = dblCost;
}
else
{
List<QePatternInstance> lSubs = vPatterns[s.Position];
for(int i = lSubs.Count - 1; i >= 0; --i)
{
QePatternInstance pi = lSubs[i];
Debug.Assert(pi.Position == s.Position);
Debug.Assert(pi.Length >= 1);
List<QePatternInstance> lNewPath =
new List<QePatternInstance>(s.Path.Count + 1);
lNewPath.AddRange(s.Path);
lNewPath.Add(pi);
Debug.Assert(lNewPath.Capacity == (s.Path.Count + 1));
QePathState sNew = new QePathState(s.Position +
pi.Length, lNewPath);
sRec.Push(sNew);
}
}
}
return (uint)Math.Ceiling(dblMinCost);
}
/// <summary>
/// Estimate the quality of a password.
/// </summary>
/// <param name="pbUnprotectedUtf8">Password to check, UTF-8 encoded.</param>
/// <returns>Estimated bit-strength of the password.</returns>
public static uint EstimatePasswordBits(byte[] pbUnprotectedUtf8)
{
if(pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; }
char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
uint uResult = EstimatePasswordBits(vChars);
MemUtil.ZeroArray<char>(vChars);
return uResult;
}
private static QeCharType GetCharType(char ch)
{
int nTypes = m_lCharTypes.Count;
Debug.Assert((nTypes > 0) && (m_lCharTypes[nTypes - 1].CharCount > 256));
for(int i = 0; i < (nTypes - 1); ++i)
{
if(m_lCharTypes[i].Contains(ch))
return m_lCharTypes[i];
}
return m_lCharTypes[nTypes - 1];
}
private static double ComputePathCost(List<QePatternInstance> l,
char[] vPassword, EntropyEncoder ecPattern, MultiEntropyEncoder mcData)
{
ecPattern.Reset();
for(int i = 0; i < l.Count; ++i)
ecPattern.Write(l[i].PatternID);
double dblPatternCost = ecPattern.GetOutputSize();
mcData.Reset();
double dblDataCost = 0.0;
foreach(QePatternInstance pi in l)
{
QeCharType tChar = pi.SingleCharType;
if(tChar != null)
{
char ch = vPassword[pi.Position];
if(!mcData.Write(tChar.TypeID, ch))
dblDataCost += pi.Cost;
}
else dblDataCost += pi.Cost;
}
dblDataCost += mcData.GetOutputSize();
return (dblPatternCost + dblDataCost);
}
private static void FindPopularPasswords(char[] vPassword,
List<QePatternInstance>[] vPatterns)
{
int n = vPassword.Length;
char[] vLower = new char[n];
char[] vLeet = new char[n];
for(int i = 0; i < n; ++i)
{
char ch = vPassword[i];
vLower[i] = char.ToLower(ch);
vLeet[i] = char.ToLower(DecodeLeetChar(ch));
}
char chErased = default(char);
Debug.Assert(chErased == char.MinValue);
int nMaxLen = Math.Min(n, PopularPasswords.MaxLength);
for(int nSubLen = nMaxLen; nSubLen >= 3; --nSubLen)
{
if(!PopularPasswords.ContainsLength(nSubLen)) continue;
char[] vSub = new char[nSubLen];
for(int i = 0; i <= (n - nSubLen); ++i)
{
if(Array.IndexOf<char>(vLower, chErased, i, nSubLen) >= 0)
continue;
Array.Copy(vLower, i, vSub, 0, nSubLen);
if(!EvalAddPopularPasswordPattern(vPatterns, vPassword,
i, vSub, 0.0))
{
Array.Copy(vLeet, i, vSub, 0, nSubLen);
if(EvalAddPopularPasswordPattern(vPatterns, vPassword,
i, vSub, 1.5))
{
Array.Clear(vLower, i, nSubLen); // Not vLeet
Debug.Assert(vLower[i] == chErased);
}
}
else
{
Array.Clear(vLower, i, nSubLen);
Debug.Assert(vLower[i] == chErased);
}
}
}
}
private static bool EvalAddPopularPasswordPattern(List<QePatternInstance>[] vPatterns,
char[] vPassword, int i, char[] vSub, double dblCostPerMod)
{
ulong uDictSize;
if(!PopularPasswords.IsPopularPassword(vSub, out uDictSize))
return false;
int n = vSub.Length;
int d = HammingDist(vSub, 0, vPassword, i, n);
double dblCost = Log2((double)uDictSize);
// dblCost += log2(n binom d)
int k = Math.Min(d, n - d);
for(int j = n; j > (n - k); --j)
dblCost += Log2(j);
for(int j = k; j >= 2; --j)
dblCost -= Log2(j);
dblCost += dblCostPerMod * (double)d;
vPatterns[i].Add(new QePatternInstance(i, n, PatternID.Dictionary,
dblCost));
return true;
}
private static char DecodeLeetChar(char chLeet)
{
if((chLeet >= '\u00C0') && (chLeet <= '\u00C6')) return 'a';
if((chLeet >= '\u00C8') && (chLeet <= '\u00CB')) return 'e';
if((chLeet >= '\u00CC') && (chLeet <= '\u00CF')) return 'i';
if((chLeet >= '\u00D2') && (chLeet <= '\u00D6')) return 'o';
if((chLeet >= '\u00D9') && (chLeet <= '\u00DC')) return 'u';
if((chLeet >= '\u00E0') && (chLeet <= '\u00E6')) return 'a';
if((chLeet >= '\u00E8') && (chLeet <= '\u00EB')) return 'e';
if((chLeet >= '\u00EC') && (chLeet <= '\u00EF')) return 'i';
if((chLeet >= '\u00F2') && (chLeet <= '\u00F6')) return 'o';
if((chLeet >= '\u00F9') && (chLeet <= '\u00FC')) return 'u';
char ch;
switch(chLeet)
{
case '4':
case '@':
case '?':
case '^':
case '\u00AA': ch = 'a'; break;
case '8':
case '\u00DF': ch = 'b'; break;
case '(':
case '{':
case '[':
case '<':
case '\u00A2':
case '\u00A9':
case '\u00C7':
case '\u00E7': ch = 'c'; break;
case '\u00D0':
case '\u00F0': ch = 'd'; break;
case '3':
case '\u20AC':
case '&':
case '\u00A3': ch = 'e'; break;
case '6':
case '9': ch = 'g'; break;
case '#': ch = 'h'; break;
case '1':
case '!':
case '|':
case '\u00A1':
case '\u00A6': ch = 'i'; break;
case '\u00D1':
case '\u00F1': ch = 'n'; break;
case '0':
case '*':
case '\u00A4': // Currency
case '\u00B0': // Degree
case '\u00D8':
case '\u00F8': ch = 'o'; break;
case '\u00AE': ch = 'r'; break;
case '$':
case '5':
case '\u00A7': ch = 's'; break;
case '+':
case '7': ch = 't'; break;
case '\u00B5': ch = 'u'; break;
case '%':
case '\u00D7': ch = 'x'; break;
case '\u00A5':
case '\u00DD':
case '\u00FD':
case '\u00FF': ch = 'y'; break;
case '2': ch = 'z'; break;
default: ch = chLeet; break;
}
return ch;
}
private static int HammingDist(char[] v1, int iOffset1,
char[] v2, int iOffset2, int nLength)
{
int nDist = 0;
for(int i = 0; i < nLength; ++i)
{
if(v1[iOffset1 + i] != v2[iOffset2 + i]) ++nDist;
}
return nDist;
}
private static void FindRepetitions(char[] vPassword,
List<QePatternInstance>[] vPatterns)
{
int n = vPassword.Length;
char[] v = new char[n];
Array.Copy(vPassword, v, n);
char chErased = char.MaxValue;
for(int m = (n / 2); m >= 3; --m)
{
for(int x1 = 0; x1 <= (n - (2 * m)); ++x1)
{
bool bFoundRep = false;
for(int x2 = (x1 + m); x2 <= (n - m); ++x2)
{
if(PartsEqual(v, x1, x2, m))
{
double dblCost = Log2(x1 + 1) + Log2(m);
vPatterns[x2].Add(new QePatternInstance(x2, m,
PatternID.Repetition, dblCost));
ErasePart(v, x2, m, ref chErased);
bFoundRep = true;
}
}
if(bFoundRep) ErasePart(v, x1, m, ref chErased);
}
}
}
private static bool PartsEqual(char[] v, int x1, int x2, int nLength)
{
for(int i = 0; i < nLength; ++i)
{
if(v[x1 + i] != v[x2 + i]) return false;
}
return true;
}
private static void ErasePart(char[] v, int i, int n, ref char chErased)
{
for(int j = 0; j < n; ++j)
{
v[i + j] = chErased;
--chErased;
}
}
private static void FindNumbers(char[] vPassword,
List<QePatternInstance>[] vPatterns)
{
int n = vPassword.Length;
StringBuilder sb = new StringBuilder();
for(int i = 0; i < n; ++i)
{
char ch = vPassword[i];
if((ch >= '0') && (ch <= '9')) sb.Append(ch);
else
{
AddNumberPattern(vPatterns, sb.ToString(), i - sb.Length);
sb.Remove(0, sb.Length);
}
}
AddNumberPattern(vPatterns, sb.ToString(), n - sb.Length);
}
private static void AddNumberPattern(List<QePatternInstance>[] vPatterns,
string strNumber, int i)
{
if(strNumber.Length <= 2) return;
int nZeros = 0;
for(int j = 0; j < strNumber.Length; ++j)
{
if(strNumber[j] != '0') break;
++nZeros;
}
double dblCost = Log2(nZeros + 1);
if(nZeros < strNumber.Length)
{
string strNonZero = strNumber.Substring(nZeros);
#if KeePassLibSD
try { dblCost += Log2(double.Parse(strNonZero)); }
catch(Exception) { Debug.Assert(false); return; }
#else
double d;
if(double.TryParse(strNonZero, out d))
dblCost += Log2(d);
else { Debug.Assert(false); return; }
#endif
}
vPatterns[i].Add(new QePatternInstance(i, strNumber.Length,
PatternID.Number, dblCost));
}
private static void FindDiffSeqs(char[] vPassword,
List<QePatternInstance>[] vPatterns)
{
int d = int.MinValue, p = 0;
string str = new string(vPassword) + new string(char.MaxValue, 1);
for(int i = 1; i < str.Length; ++i)
{
int dCur = (int)str[i] - (int)str[i - 1];
if(dCur != d)
{
if((i - p) >= 3) // At least 3 chars involved
{
QeCharType ct = GetCharType(str[p]);
double dblCost = ct.CharSize + Log2(i - p - 1);
vPatterns[p].Add(new QePatternInstance(p,
i - p, PatternID.DiffSeq, dblCost));
}
d = dCur;
p = i - 1;
}
}
}
private static double Log2(double dblValue)
{
#if KeePassLibSD
return (Math.Log(dblValue) / Math.Log(2.0));
#else
return Math.Log(dblValue, 2.0);
#endif
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
/*
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;
namespace KeePassLib.Delegates
{
/// <summary>
/// Function definition of a method that performs an action on a group.
/// When traversing the internal tree, this function will be invoked
/// for all visited groups.
/// </summary>
/// <param name="pg">Currently visited group.</param>
/// <returns>You must return <c>true</c> if you want to continue the
/// traversal. If you want to immediately stop the whole traversal,
/// return <c>false</c>.</returns>
public delegate bool GroupHandler(PwGroup pg);
/// <summary>
/// Function definition of a method that performs an action on an entry.
/// When traversing the internal tree, this function will be invoked
/// for all visited entries.
/// </summary>
/// <param name="pe">Currently visited entry.</param>
/// <returns>You must return <c>true</c> if you want to continue the
/// traversal. If you want to immediately stop the whole traversal,
/// return <c>false</c>.</returns>
public delegate bool EntryHandler(PwEntry pe);
public delegate void VoidDelegate();
public delegate string StrPwEntryDelegate(string str, PwEntry pe);
}

View File

@@ -0,0 +1,28 @@
using System.IO;
using KeePassLib.Interfaces;
using KeePassLib.Keys;
namespace KeePassLib
{
public interface IDatabaseFormat
{
void PopulateDatabaseFromStream(PwDatabase db, Stream s, IStatusLogger slLogger);
byte[] HashOfLastStream { get; }
bool CanWrite { get; }
string SuccessMessage { get; }
void Save(PwDatabase kpDatabase, Stream stream);
bool CanHaveEntriesInRootGroup { get; }
bool CanHaveMultipleAttachments { get; }
bool CanHaveCustomFields { get; }
bool HasDefaultUsername { get; }
bool HasDatabaseName { get; }
bool SupportsAttachmentKeys { get; }
bool SupportsTags { get; }
bool SupportsOverrideUrl { get; }
bool CanRecycle { get; }
bool SupportsTemplates { get; }
}
}

View File

@@ -0,0 +1,37 @@
/*
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;
namespace KeePassLib.Interfaces
{
/// <summary>
/// Interface for objects that are deeply cloneable.
/// </summary>
/// <typeparam name="T">Reference type.</typeparam>
public interface IDeepCloneable<T> where T : class
{
/// <summary>
/// Deeply clone the object.
/// </summary>
/// <returns>Cloned object.</returns>
T CloneDeep();
}
}

View File

@@ -0,0 +1,105 @@
/*
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;
namespace KeePassLib.Interfaces
{
/// <summary>
/// Status message types.
/// </summary>
public enum LogStatusType
{
/// <summary>
/// Default type: simple information type.
/// </summary>
Info = 0,
/// <summary>
/// Warning message.
/// </summary>
Warning,
/// <summary>
/// Error message.
/// </summary>
Error,
/// <summary>
/// Additional information. Depends on lines above.
/// </summary>
AdditionalInfo
}
/// <summary>
/// Status logging interface.
/// </summary>
public interface IStatusLogger
{
/// <summary>
/// Function which needs to be called when logging is started.
/// </summary>
/// <param name="strOperation">This string should roughly describe
/// the operation, of which the status is logged.</param>
/// <param name="bWriteOperationToLog">Specifies whether the
/// operation is written to the log or not.</param>
void StartLogging(string strOperation, bool bWriteOperationToLog);
/// <summary>
/// Function which needs to be called when logging is ended
/// (i.e. when no more messages will be logged and when the
/// percent value won't change any more).
/// </summary>
void EndLogging();
/// <summary>
/// Set the current progress in percent.
/// </summary>
/// <param name="uPercent">Percent of work finished.</param>
/// <returns>Returns <c>true</c> if the caller should continue
/// the current work.</returns>
bool SetProgress(uint uPercent);
/// <summary>
/// Set the current status text.
/// </summary>
/// <param name="strNewText">Status text.</param>
/// <param name="lsType">Type of the message.</param>
/// <returns>Returns <c>true</c> if the caller should continue
/// the current work.</returns>
bool SetText(string strNewText, LogStatusType lsType);
/// <summary>
/// Check if the user cancelled the current work.
/// </summary>
/// <returns>Returns <c>true</c> if the caller should continue
/// the current work.</returns>
bool ContinueWork();
}
public sealed class NullStatusLogger : IStatusLogger
{
public void StartLogging(string strOperation, bool bWriteOperationToLog) { }
public void EndLogging() { }
public bool SetProgress(uint uPercent) { return true; }
public bool SetText(string strNewText, LogStatusType lsType) { return true; }
public bool ContinueWork() { return true; }
}
}

View File

@@ -0,0 +1,42 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 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;
namespace KeePassLib.Interfaces
{
public interface IStructureItem : ITimeLogger // Provides LocationChanged
{
PwUuid Uuid
{
get;
set;
}
PwGroup ParentGroup
{
get;
}
PwUuid PreviousParentGroup
{
get;
}
}
}

View File

@@ -0,0 +1,105 @@
/*
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;
namespace KeePassLib.Interfaces
{
/// <summary>
/// Interface for objects that support various times (creation time, last
/// access time, last modification time and expiry time). Offers
/// several helper functions (for example a function to touch the current
/// object).
/// </summary>
public interface ITimeLogger
{
/// <summary>
/// The date/time when the object was created.
/// </summary>
DateTime CreationTime
{
get;
set;
}
/// <summary>
/// The date/time when the object was last modified.
/// </summary>
DateTime LastModificationTime
{
get;
set;
}
/// <summary>
/// The date/time when the object was last accessed.
/// </summary>
DateTime LastAccessTime
{
get;
set;
}
/// <summary>
/// The date/time when the object expires.
/// </summary>
DateTime ExpiryTime
{
get;
set;
}
/// <summary>
/// Flag that determines if the object does expire.
/// </summary>
bool Expires
{
get;
set;
}
/// <summary>
/// Get or set the usage count of the object. To increase the usage
/// count by one, use the <c>Touch</c> function.
/// </summary>
ulong UsageCount
{
get;
set;
}
/// <summary>
/// The date/time when the location of the object was last changed.
/// </summary>
DateTime LocationChanged
{
get;
set;
}
/// <summary>
/// Touch the object. This function updates the internal last access
/// time. If the <paramref name="bModified" /> parameter is <c>true</c>,
/// the last modification time gets updated, too. Each time you call
/// <c>Touch</c>, the usage count of the object is increased by one.
/// </summary>
/// <param name="bModified">Update last modification time.</param>
void Touch(bool bModified);
}
}

View File

@@ -0,0 +1,37 @@
/*
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.Text;
namespace KeePassLib.Interfaces
{
public interface IUIOperations
{
/// <summary>
/// Let the user interface save the current database.
/// </summary>
/// <param name="bForceSave">If <c>true</c>, the UI will not ask for
/// whether to synchronize or overwrite, it'll simply overwrite the
/// file.</param>
/// <returns>Returns <c>true</c> if the file has been saved.</returns>
bool UIFileSave(bool bForceSave);
}
}

View File

@@ -0,0 +1,33 @@
/*
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.Text;
using System.IO;
using System.Xml;
namespace KeePassLib.Interfaces
{
public interface IXmlSerializerEx
{
void Serialize(XmlWriter xmlWriter, object o);
object Deserialize(Stream s);
}
}

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\values\Strings.xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\keepass2android-appSdkStyle\keepass2android-appSdkStyle.csproj" />
<ProjectReference Include="..\KP2AKdbLibraryBindingSdkStyle\KP2AKdbLibraryBindingSdkStyle.csproj" />
</ItemGroup>
<ItemGroup>
<AndroidResource Update="Resources\values\Strings.xml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<Compile Update="Serialization\IOConnection.cs">
<SubType>Component</SubType>
</Compile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,279 @@
/*
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 KeePassLib.Cryptography;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.Keys
{
/// <summary>
/// Represents a key. A key can be build up using several user key data sources
/// like a password, a key file, the currently logged on user credentials,
/// the current computer ID, etc.
/// </summary>
public sealed class CompositeKey
{
private List<IUserKey> m_vUserKeys = new List<IUserKey>();
/// <summary>
/// List of all user keys contained in the current composite key.
/// </summary>
public IEnumerable<IUserKey> UserKeys
{
get { return m_vUserKeys; }
}
public uint UserKeyCount
{
get { return (uint)m_vUserKeys.Count; }
}
/// <summary>
/// Construct a new, empty key object.
/// </summary>
public CompositeKey()
{
}
// /// <summary>
// /// Deconstructor, clears up the key.
// /// </summary>
// ~CompositeKey()
// {
// Clear();
// }
// /// <summary>
// /// Clears the key. This function also erases all previously stored
// /// user key data objects.
// /// </summary>
// public void Clear()
// {
// foreach(IUserKey pKey in m_vUserKeys)
// pKey.Clear();
// m_vUserKeys.Clear();
// }
/// <summary>
/// Add a user key.
/// </summary>
/// <param name="pKey">User key to add.</param>
public void AddUserKey(IUserKey pKey)
{
Debug.Assert(pKey != null); if(pKey == null) throw new ArgumentNullException("pKey");
m_vUserKeys.Add(pKey);
}
/// <summary>
/// Remove a user key.
/// </summary>
/// <param name="pKey">User key to remove.</param>
/// <returns>Returns <c>true</c> if the key was removed successfully.</returns>
public bool RemoveUserKey(IUserKey pKey)
{
Debug.Assert(pKey != null); if(pKey == null) throw new ArgumentNullException("pKey");
Debug.Assert(m_vUserKeys.IndexOf(pKey) >= 0);
return m_vUserKeys.Remove(pKey);
}
/// <summary>
/// Test whether the composite key contains a specific type of
/// user keys (password, key file, ...). If at least one user
/// key of that type is present, the function returns <c>true</c>.
/// </summary>
/// <param name="tUserKeyType">User key type.</param>
/// <returns>Returns <c>true</c>, if the composite key contains
/// a user key of the specified type.</returns>
public bool ContainsType(Type tUserKeyType)
{
Debug.Assert(tUserKeyType != null);
if(tUserKeyType == null) throw new ArgumentNullException("tUserKeyType");
foreach(IUserKey pKey in m_vUserKeys)
{
if(pKey == null) { Debug.Assert(false); continue; }
#if KeePassUAP
if(pKey.GetType() == tUserKeyType)
return true;
#else
if(tUserKeyType.IsInstanceOfType(pKey))
return true;
#endif
}
return false;
}
/// <summary>
/// Get the first user key of a specified type.
/// </summary>
/// <param name="tUserKeyType">Type of the user key to get.</param>
/// <returns>Returns the first user key of the specified type
/// or <c>null</c> if no key of that type is found.</returns>
public IUserKey GetUserKey(Type tUserKeyType)
{
Debug.Assert(tUserKeyType != null);
if(tUserKeyType == null) throw new ArgumentNullException("tUserKeyType");
foreach(IUserKey pKey in m_vUserKeys)
{
if(pKey == null) { Debug.Assert(false); continue; }
#if KeePassUAP
if(pKey.GetType() == tUserKeyType)
return pKey;
#else
if(tUserKeyType.IsInstanceOfType(pKey))
return pKey;
#endif
}
return null;
}
public T GetUserKey<T>() where T : IUserKey
{
return (T) GetUserKey(typeof (T));
}
/// <summary>
/// Creates the composite key from the supplied user key sources (password,
/// key file, user account, computer ID, etc.).
/// </summary>
private byte[] CreateRawCompositeKey32(byte[] mPbMasterSeed, byte[] mPbKdfSeed)
{
ValidateUserKeys();
// Concatenate user key data
List<byte[]> lData = new List<byte[]>();
int cbData = 0;
foreach(IUserKey pKey in m_vUserKeys)
{
if (pKey is ISeedBasedUserKey)
((ISeedBasedUserKey)pKey).SetParams(mPbMasterSeed, mPbKdfSeed);
ProtectedBinary b = pKey.KeyData;
if(b != null)
{
byte[] pbKeyData = b.ReadData();
lData.Add(pbKeyData);
cbData += pbKeyData.Length;
}
}
byte[] pbAllData = new byte[cbData];
int p = 0;
foreach(byte[] pbData in lData)
{
Array.Copy(pbData, 0, pbAllData, p, pbData.Length);
p += pbData.Length;
MemUtil.ZeroByteArray(pbData);
}
Debug.Assert(p == cbData);
byte[] pbHash = CryptoUtil.HashSha256(pbAllData);
MemUtil.ZeroByteArray(pbAllData);
return pbHash;
}
/// <summary>
/// Generate a 32-byte (256-bit) key from the composite key.
/// </summary>
public ProtectedBinary GenerateKey32(KdfParameters p, byte[] mPbMasterSeed)
{
if(p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); }
KdfEngine kdf = KdfPool.Get(p.KdfUuid);
if (kdf == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
"UUID: " + p.KdfUuid.ToHexString() + ".");
byte[] pbRaw32 = CreateRawCompositeKey32(mPbMasterSeed, kdf.GetSeed(p));
if((pbRaw32 == null) || (pbRaw32.Length != 32))
{ Debug.Assert(false); return null; }
byte[] pbTrf32 = kdf.Transform(pbRaw32, p);
if(pbTrf32 == null) { Debug.Assert(false); return null; }
if(pbTrf32.Length != 32)
{
Debug.Assert(false);
pbTrf32 = CryptoUtil.HashSha256(pbTrf32);
}
ProtectedBinary pbRet = new ProtectedBinary(true, pbTrf32);
MemUtil.ZeroByteArray(pbTrf32);
MemUtil.ZeroByteArray(pbRaw32);
return pbRet;
}
private void ValidateUserKeys()
{
int nAccounts = 0;
foreach(IUserKey uKey in m_vUserKeys)
{
if(uKey is KcpUserAccount)
++nAccounts;
}
if(nAccounts >= 2)
{
Debug.Assert(false);
throw new InvalidOperationException();
}
}
}
public interface ISeedBasedUserKey
{
void SetParams(byte[] masterSeed, byte[] mPbKdfSeed);
}
public sealed class InvalidCompositeKeyException : Exception
{
public override string Message
{
get
{
return KLRes.InvalidCompositeKey + MessageService.NewParagraph +
KLRes.InvalidCompositeKeyHint;
}
}
/// <summary>
/// Construct a new invalid composite key exception.
/// </summary>
public InvalidCompositeKeyException()
{
}
}
}

View File

@@ -0,0 +1,48 @@
/*
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 KeePassLib.Security;
namespace KeePassLib.Keys
{
/// <summary>
/// Interface to a user key, like a password, key file data, etc.
/// </summary>
public interface IUserKey
{
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
ProtectedBinary KeyData
{
get;
}
// /// <summary>
// /// Clear the key and securely erase all security-critical information.
// /// </summary>
// void Clear();
uint GetMinKdbxVersion();
}
}

View File

@@ -0,0 +1,73 @@
/*
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 KeePassLib.Cryptography;
using KeePassLib.Security;
namespace KeePassLib.Keys
{
public sealed class KcpCustomKey : IUserKey
{
private readonly string m_strName;
private ProtectedBinary m_pbKey;
/// <summary>
/// Name of the provider that generated the custom key.
/// </summary>
public string Name
{
get { return m_strName; }
}
public ProtectedBinary KeyData
{
get { return m_pbKey; }
}
public uint GetMinKdbxVersion()
{
return 0;
}
public KcpCustomKey(string strName, byte[] pbKeyData, bool bPerformHash)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
Debug.Assert(pbKeyData != null); if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
m_strName = strName;
if(bPerformHash)
{
byte[] pbRaw = CryptoUtil.HashSha256(pbKeyData);
m_pbKey = new ProtectedBinary(true, pbRaw);
}
else m_pbKey = new ProtectedBinary(true, pbKeyData);
}
// public void Clear()
// {
// m_pbKey = null;
// }
}
}

View File

@@ -0,0 +1,281 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 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.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using KeePassLib.Cryptography;
using KeePassLib.Resources;
using KeePassLib.Serialization;
using KeePassLib.Utility;
namespace KeePassLib.Keys
{
[XmlType("KeyFile")]
public sealed class KfxFile
{
private const ulong KfxVersionCriticalMask = 0xFFFF000000000000UL;
private const int KfxDataHashLength = 4;
private KfxMeta m_meta = new KfxMeta();
public KfxMeta Meta
{
get { return m_meta; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_meta = value;
}
}
private KfxKey m_key = new KfxKey();
public KfxKey Key
{
get { return m_key; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_key = value;
}
}
public static KfxFile Create(ulong uVersion, byte[] pbKey, byte[] pbHash)
{
if(pbKey == null) throw new ArgumentNullException("pbKey");
if(pbKey.Length == 0) throw new ArgumentOutOfRangeException("pbKey");
if(uVersion == 0) uVersion = 0x0002000000000000;
// Null hash: generate one, empty hash: store no hash
if(pbHash == null) pbHash = HashData(pbKey);
VerifyHash(pbKey, pbHash);
KfxFile kf = new KfxFile();
if(uVersion == 0x0001000000000000)
kf.Meta.Version = "1.00"; // KeePass <= 2.46 used two zeros
else kf.Meta.Version = StrUtil.VersionToString(uVersion, 2);
if(uVersion == 0x0001000000000000)
kf.Key.Data.Value = Convert.ToBase64String(pbKey);
else if(uVersion == 0x0002000000000000)
{
kf.Key.Data.Value = FormatKeyHex(pbKey, 3);
if(pbHash.Length != 0)
kf.Key.Data.Hash = MemUtil.ByteArrayToHexString(pbHash);
}
else throw new NotSupportedException(KLRes.FileVersionUnsupported);
return kf;
}
internal static KfxFile Create(ulong uVersion, string strKey, string strHash)
{
byte[] pbKey = ParseKey(uVersion, strKey);
byte[] pbHash = ((strHash != null) ? ParseHash(strHash) : null);
return Create(uVersion, pbKey, pbHash);
}
internal static bool CanLoad(string strFilePath)
{
if(string.IsNullOrEmpty(strFilePath)) { Debug.Assert(false); return false; }
try
{
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
using(Stream s = IOConnection.OpenRead(ioc))
{
return (Load(s) != null);
}
}
catch(Exception) { }
return false;
}
public static KfxFile Load(Stream s)
{
return XmlUtilEx.Deserialize<KfxFile>(s);
}
public void Save(Stream s)
{
XmlUtilEx.Serialize<KfxFile>(s, this, true);
}
private static string FormatKeyHex(byte[] pb, int cTabs)
{
StringBuilder sb = new StringBuilder();
string str = MemUtil.ByteArrayToHexString(pb);
for(int i = 0; i < str.Length; ++i)
{
if((i & 0x1F) == 0)
{
sb.AppendLine();
sb.Append('\t', cTabs);
}
else if((i & 0x07) == 0) sb.Append(' ');
sb.Append(str[i]);
}
sb.AppendLine();
if(cTabs > 0) sb.Append('\t', cTabs - 1);
return sb.ToString();
}
private ulong GetVersion()
{
string str = m_meta.Version;
if(string.IsNullOrEmpty(str)) return 0;
return StrUtil.ParseVersion(str);
}
public byte[] GetKey()
{
ulong uVersion = GetVersion();
byte[] pbKey = ParseKey(uVersion, m_key.Data.Value);
if((pbKey == null) || (pbKey.Length == 0))
throw new FormatException(KLRes.FileCorrupted);
byte[] pbHash = ParseHash(m_key.Data.Hash);
VerifyHash(pbKey, pbHash);
return pbKey;
}
private static byte[] HashData(byte[] pb)
{
return MemUtil.Mid(CryptoUtil.HashSha256(pb), 0, KfxDataHashLength);
}
private static void VerifyHash(byte[] pbKey, byte[] pbHash)
{
// The hash is optional; empty hash means success
if((pbHash == null) || (pbHash.Length == 0)) return;
byte[] pbHashCmp = HashData(pbKey);
if(!MemUtil.ArraysEqual(pbHash, pbHashCmp))
throw new Exception("Keyfile hash mismatch!");
}
private static byte[] ParseKey(ulong uVersion, string strKey)
{
if(strKey == null) throw new ArgumentNullException("strKey");
strKey = StrUtil.RemoveWhiteSpace(strKey);
if(string.IsNullOrEmpty(strKey)) return MemUtil.EmptyByteArray;
uVersion &= KfxVersionCriticalMask;
byte[] pbKey;
if(uVersion == 0x0001000000000000)
pbKey = Convert.FromBase64String(strKey);
else if(uVersion == 0x0002000000000000)
pbKey = ParseHex(strKey);
else throw new NotSupportedException(KLRes.FileVersionUnsupported);
return pbKey;
}
private static byte[] ParseHash(string strHash)
{
return ParseHex(strHash);
}
private static byte[] ParseHex(string str)
{
if(str == null) throw new ArgumentNullException("str");
if(str.Length == 0) return MemUtil.EmptyByteArray;
if(((str.Length & 1) != 0) || !StrUtil.IsHexString(str, true))
throw new FormatException();
return MemUtil.HexStringToByteArray(str);
}
}
public sealed class KfxMeta
{
private string m_strVersion = string.Empty;
[DefaultValue("")]
public string Version
{
get { return m_strVersion; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strVersion = value;
}
}
}
public sealed class KfxKey
{
private KfxData m_data = new KfxData();
public KfxData Data
{
get { return m_data; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_data = value;
}
}
}
public sealed class KfxData
{
private string m_strHash = string.Empty;
[DefaultValue("")]
[XmlAttribute("Hash")]
public string Hash
{
get { return m_strHash; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strHash = value;
}
}
private string m_strValue = string.Empty;
[DefaultValue("")]
[XmlText]
public string Value
{
get { return m_strValue; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strValue = value;
}
}
}
}

View File

@@ -0,0 +1,358 @@
/*
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.Diagnostics;
using System.IO;
using System.Security;
using System.Text;
using System.Xml;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography;
using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using InvalidDataException = KeePassLib.Serialization.InvalidDataException;
namespace KeePassLib.Keys
{
/// <summary>
/// Key files as provided by the user.
/// </summary>
public sealed class KcpKeyFile : IUserKey
{
private IOConnectionInfo m_ioc;
private ProtectedBinary m_pbKeyData;
private ProtectedBinary m_pbFileData;
/// <summary>
/// Path to the key file.
/// </summary>
public string Path
{
get { return m_ioc.Path; }
}
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
public ProtectedBinary KeyData
{
get { return m_pbKeyData; }
}
public uint GetMinKdbxVersion()
{
return 0;
}
public IOConnectionInfo Ioc
{
get { return m_ioc; }
}
public ProtectedBinary RawFileData
{
get { return m_pbFileData; }
}
public KcpKeyFile(string strKeyFile)
{
Construct(IOConnectionInfo.FromPath(strKeyFile), false);
}
public KcpKeyFile(string strKeyFile, bool bThrowIfDbFile)
{
Construct(IOConnectionInfo.FromPath(strKeyFile), bThrowIfDbFile);
}
public KcpKeyFile(IOConnectionInfo iocKeyFile)
{
Construct(iocKeyFile, false);
}
public KcpKeyFile(IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
{
Construct(iocKeyFile, bThrowIfDbFile);
}
public KcpKeyFile(byte[] keyFileContents, IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
{
Construct(keyFileContents, iocKeyFile, bThrowIfDbFile);
}
private void Construct(byte[] pbFileData, IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
{
if (pbFileData == null) throw new Java.IO.FileNotFoundException();
m_pbFileData = new ProtectedBinary(true, pbFileData);
if(bThrowIfDbFile && (pbFileData.Length >= 8))
{
uint uSig1 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 0, 4));
uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4));
if(((uSig1 == KdbxFile.FileSignature1) &&
(uSig2 == KdbxFile.FileSignature2)) ||
((uSig1 == KdbxFile.FileSignaturePreRelease1) &&
(uSig2 == KdbxFile.FileSignaturePreRelease2)) ||
((uSig1 == KdbxFile.FileSignatureOld1) &&
(uSig2 == KdbxFile.FileSignatureOld2)))
#if KeePassLibSD
throw new Exception(KLRes.KeyFileDbSel);
#else
throw new InvalidDataException(KLRes.KeyFileDbSel);
#endif
}
byte[] pbKey = LoadKeyFile(pbFileData);
if (pbKey == null) throw new InvalidOperationException();
m_ioc = iocKeyFile;
m_pbKeyData = new ProtectedBinary(true, pbKey);
MemUtil.ZeroByteArray(pbKey);
}
private void Construct(IOConnectionInfo iocFile, bool bThrowIfDbFile)
{
byte[] pbFileData = IOConnection.ReadFile(iocFile);
Construct(pbFileData, iocFile, bThrowIfDbFile);
}
// public void Clear()
// {
// m_strPath = string.Empty;
// m_pbKeyData = null;
// }
private static byte[] LoadKeyFile(byte[] pbFileData)
{
if (pbFileData == null) throw new ArgumentNullException("pbFileData");
byte[] pbKey = LoadKeyFileXml(pbFileData);
if (pbKey != null) return pbKey;
int cb = pbFileData.Length;
if (cb == 32) return pbFileData;
if (cb == 64)
{
pbKey = LoadKeyFileHex(pbFileData);
if (pbKey != null) return pbKey;
}
return CryptoUtil.HashSha256(pbFileData);
}
private static byte[] LoadKeyFileXml(byte[] pbFileData)
{
KfxFile kf;
try
{
using (MemoryStream ms = new MemoryStream(pbFileData, false))
{
kf = KfxFile.Load(ms);
}
}
catch (Exception) { return null; }
// We have a syntactically valid XML key file;
// failing to verify the key should throw an exception
return ((kf != null) ? kf.GetKey() : null);
}
private static byte[] LoadKeyFileHex(byte[] pbFileData)
{
if (pbFileData == null) { Debug.Assert(false); return null; }
try
{
int cc = pbFileData.Length;
if ((cc & 1) != 0) { Debug.Assert(false); return null; }
if (!StrUtil.IsHexString(pbFileData, true)) return null;
string strHex = StrUtil.Utf8.GetString(pbFileData);
return MemUtil.HexStringToByteArray(strHex);
}
catch (Exception) { Debug.Assert(false); }
return null;
}
/// <summary>
/// Create a new, random key-file.
/// </summary>
/// <param name="strFilePath">Path where the key-file should be saved to.
/// If the file exists already, it will be overwritten.</param>
/// <param name="pbAdditionalEntropy">Additional entropy used to generate
/// the random key. May be <c>null</c> (in this case only the KeePass-internal
/// random number generator is used).</param>
/// <returns>Returns a <c>FileSaveResult</c> error code.</returns>
public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
{
byte[] pbKey32 = CryptoRandom.Instance.GetRandomBytes(32);
if(pbKey32 == null) throw new SecurityException();
byte[] pbFinalKey32;
if((pbAdditionalEntropy == null) || (pbAdditionalEntropy.Length == 0))
pbFinalKey32 = pbKey32;
else
{
using(MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, pbAdditionalEntropy);
MemUtil.Write(ms, pbKey32);
pbFinalKey32 = CryptoUtil.HashSha256(ms.ToArray());
}
}
CreateXmlKeyFile(strFilePath, pbFinalKey32);
}
// ================================================================
// XML Key Files
// ================================================================
// Sample XML file:
// <?xml version="1.0" encoding="utf-8"?>
// <KeyFile>
// <Meta>
// <Version>1.00</Version>
// </Meta>
// <Key>
// <Data>ySFoKuCcJblw8ie6RkMBdVCnAf4EedSch7ItujK6bmI=</Data>
// </Key>
// </KeyFile>
private const string RootElementName = "KeyFile";
private const string MetaElementName = "Meta";
private const string VersionElementName = "Version";
private const string KeyElementName = "Key";
private const string KeyDataElementName = "Data";
private static byte[] LoadXmlKeyFile(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
MemoryStream ms = new MemoryStream(pbFileData, false);
byte[] pbKeyData = null;
try
{
XmlDocument doc = new XmlDocument() { XmlResolver = null };
doc.Load(ms);
XmlElement el = doc.DocumentElement;
if((el == null) || !el.Name.Equals(RootElementName)) return null;
if(el.ChildNodes.Count < 2) return null;
foreach(XmlNode xmlChild in el.ChildNodes)
{
if(xmlChild.Name.Equals(MetaElementName)) { } // Ignore Meta
else if(xmlChild.Name == KeyElementName)
{
foreach(XmlNode xmlKeyChild in xmlChild.ChildNodes)
{
if(xmlKeyChild.Name == KeyDataElementName)
{
if(pbKeyData == null)
pbKeyData = Convert.FromBase64String(xmlKeyChild.InnerText);
}
}
}
}
}
catch(Exception) { pbKeyData = null; }
finally { ms.Close(); }
return pbKeyData;
}
private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData)
{
Debug.Assert(strFile != null);
if(strFile == null) throw new ArgumentNullException("strFile");
Debug.Assert(pbKeyData != null);
if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
Stream sOut = IOConnection.OpenWrite(ioc);
#if KeePassUAP
XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = StrUtil.Utf8;
xws.Indent = false;
XmlWriter xtw = XmlWriter.Create(sOut, xws);
#else
XmlTextWriter xtw = new XmlTextWriter(sOut, StrUtil.Utf8);
#endif
xtw.WriteStartDocument();
xtw.WriteWhitespace("\r\n");
xtw.WriteStartElement(RootElementName); // KeyFile
xtw.WriteWhitespace("\r\n\t");
xtw.WriteStartElement(MetaElementName); // Meta
xtw.WriteWhitespace("\r\n\t\t");
xtw.WriteStartElement(VersionElementName); // Version
xtw.WriteString("1.00");
xtw.WriteEndElement(); // End Version
xtw.WriteWhitespace("\r\n\t");
xtw.WriteEndElement(); // End Meta
xtw.WriteWhitespace("\r\n\t");
xtw.WriteStartElement(KeyElementName); // Key
xtw.WriteWhitespace("\r\n\t\t");
xtw.WriteStartElement(KeyDataElementName); // Data
xtw.WriteString(Convert.ToBase64String(pbKeyData));
xtw.WriteEndElement(); // End Data
xtw.WriteWhitespace("\r\n\t");
xtw.WriteEndElement(); // End Key
xtw.WriteWhitespace("\r\n");
xtw.WriteEndElement(); // RootElementName
xtw.WriteWhitespace("\r\n");
xtw.WriteEndDocument(); // End KeyFile
xtw.Close();
sOut.Close();
}
/// <summary>
/// Allows to change the ioc value (without reloading the data, assuming it's the same content)
/// </summary>
/// <param name="newIoc"></param>
public void ResetIoc(IOConnectionInfo newIoc)
{
m_ioc = newIoc;
}
}
}

View File

@@ -0,0 +1,106 @@
/*
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.Diagnostics;
using System.Text;
using KeePassLib.Cryptography;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.Keys
{
/// <summary>
/// Master password / passphrase as provided by the user.
/// </summary>
public sealed class KcpPassword : IUserKey
{
private ProtectedString m_psPassword;
private ProtectedBinary m_pbKeyData;
/// <summary>
/// Get the password as protected string.
/// </summary>
public ProtectedString Password
{
get { return m_psPassword; }
}
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
public ProtectedBinary KeyData
{
get { return m_pbKeyData; }
}
public uint GetMinKdbxVersion()
{
return 0;
}
public KcpPassword(byte[] pbPasswordUtf8)
{
SetKey(pbPasswordUtf8);
}
public KcpPassword(string strPassword)
{
SetKey(StrUtil.Utf8.GetBytes(strPassword));
}
private void SetKey(byte[] pbPasswordUtf8)
{
Debug.Assert(pbPasswordUtf8 != null);
if(pbPasswordUtf8 == null) throw new ArgumentNullException("pbPasswordUtf8");
#if (DEBUG && !KeePassLibSD)
Debug.Assert(ValidatePassword(pbPasswordUtf8));
#endif
byte[] pbRaw = CryptoUtil.HashSha256(pbPasswordUtf8);
m_psPassword = new ProtectedString(true, pbPasswordUtf8);
m_pbKeyData = new ProtectedBinary(true, pbRaw);
}
// public void Clear()
// {
// m_psPassword = null;
// m_pbKeyData = null;
// }
#if (DEBUG && !KeePassLibSD)
private static bool ValidatePassword(byte[] pb)
{
try
{
string str = StrUtil.Utf8.GetString(pb);
return str.IsNormalized(NormalizationForm.FormC);
}
catch(Exception) { Debug.Assert(false); }
return false;
}
#endif
}
}

View File

@@ -0,0 +1,115 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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.Diagnostics;
using System.Security;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography;
using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.Keys
{
/// <summary>
/// A user key depending on the currently logged on Windows user account.
/// </summary>
public sealed class KcpUserAccount : IUserKey
{
private ProtectedBinary m_pbKeyData = null;
// Constant initialization vector (unique for KeePass)
private static readonly byte[] m_pbEntropy = new byte[]{
0xDE, 0x13, 0x5B, 0x5F, 0x18, 0xA3, 0x46, 0x70,
0xB2, 0x57, 0x24, 0x29, 0x69, 0x88, 0x98, 0xE6
};
private const string UserKeyFileName = "ProtectedUserKey.bin";
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
public ProtectedBinary KeyData
{
get { return m_pbKeyData; }
}
public uint GetMinKdbxVersion()
{
return 0;
}
/// <summary>
/// Construct a user account key.
/// </summary>
public KcpUserAccount()
{
throw new NotSupportedException("DataProtection not supported on MonoForAndroid!");
}
// public void Clear()
// {
// m_pbKeyData = null;
// }
private static byte[] LoadUserKey(bool bShowWarning)
{
byte[] pbKey = null;
#if !KeePassLibSD
try
{
throw new NotSupportedException("DataProtection not supported on MonoForAndroid!");
}
catch (Exception exLoad)
{
if (bShowWarning) MessageService.ShowWarning(exLoad);
pbKey = null;
}
#endif
return pbKey;
}
private static byte[] CreateUserKey()
{
byte[] pbKey = null;
#if !KeePassLibSD
try
{
throw new NotSupportedException("DataProtection not supported on MonoForAndroid!");
}
catch (Exception) { pbKey = null; }
#endif
return pbKey;
}
}
}

View File

@@ -0,0 +1,152 @@
/*
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.Text;
using KeePassLib.Serialization;
namespace KeePassLib.Keys
{
public sealed class KeyProviderQueryContext
{
private IOConnectionInfo m_ioInfo;
public IOConnectionInfo DatabaseIOInfo
{
get { return m_ioInfo; }
}
public string DatabasePath
{
get { return m_ioInfo.Path; }
}
private bool m_bCreatingNewKey;
public bool CreatingNewKey
{
get { return m_bCreatingNewKey; }
}
private bool m_bSecDesktop;
public bool IsOnSecureDesktop
{
get { return m_bSecDesktop; }
}
public KeyProviderQueryContext(IOConnectionInfo ioInfo, bool bCreatingNewKey,
bool bOnSecDesktop)
{
if(ioInfo == null) throw new ArgumentNullException("ioInfo");
m_ioInfo = ioInfo.CloneDeep();
m_bCreatingNewKey = bCreatingNewKey;
m_bSecDesktop = bOnSecDesktop;
}
}
public abstract class KeyProvider
{
/// <summary>
/// Name of your key provider (should be unique).
/// </summary>
public abstract string Name
{
get;
}
/// <summary>
/// Property indicating whether the provider is exclusive.
/// If the provider is exclusive, KeePass doesn't allow other
/// key sources (master password, Windows user account, ...)
/// to be combined with the provider.
/// Key providers typically should return <c>false</c>
/// (to allow non-exclusive use), i.e. don't override this
/// property.
/// </summary>
public virtual bool Exclusive
{
get { return false; }
}
/// <summary>
/// Property that specifies whether the returned key data
/// gets hashed by KeePass first or is written directly to
/// the user key data stream.
/// Standard key provider plugins should return <c>false</c>
/// (i.e. don't overwrite this property). Returning <c>true</c>
/// may cause severe security problems and is highly
/// discouraged.
/// </summary>
public virtual bool DirectKey
{
get { return false; }
}
// public virtual PwIcon ImageIndex
// {
// get { return PwIcon.UserKey; }
// }
/// <summary>
/// This property specifies whether the <c>GetKey</c> method might
/// show a form or dialog. If there is any chance that the method shows
/// one, this property must return <c>true</c>. Only if it's guaranteed
/// that the <c>GetKey</c> method doesn't show any form or dialog, this
/// property should return <c>false</c>.
/// </summary>
public virtual bool GetKeyMightShowGui
{
get { return true; }
}
/// <summary>
/// This property specifies whether the key provider is compatible
/// with the secure desktop mode. This almost never is the case,
/// so you usually won't override this property.
/// </summary>
public virtual bool SecureDesktopCompatible
{
get { return false; }
}
public abstract byte[] GetKey(KeyProviderQueryContext ctx);
}
#if DEBUG
public sealed class SampleKeyProvider : KeyProvider
{
public override string Name
{
get { return "Built-In Sample Key Provider"; }
}
// Do not just copy this to your own key provider class! See above.
public override bool GetKeyMightShowGui
{
get { return false; }
}
public override byte[] GetKey(KeyProviderQueryContext ctx)
{
return new byte[]{ 2, 3, 5, 7, 11, 13 };
}
}
#endif
}

View File

@@ -0,0 +1,105 @@
/*
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.Text;
using System.Diagnostics;
namespace KeePassLib.Keys
{
public sealed class KeyProviderPool : IEnumerable<KeyProvider>
{
private List<KeyProvider> m_vProviders = new List<KeyProvider>();
public int Count
{
get { return m_vProviders.Count; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vProviders.GetEnumerator();
}
public IEnumerator<KeyProvider> GetEnumerator()
{
return m_vProviders.GetEnumerator();
}
public void Add(KeyProvider prov)
{
Debug.Assert(prov != null); if(prov == null) throw new ArgumentNullException("prov");
m_vProviders.Add(prov);
}
public bool Remove(KeyProvider prov)
{
Debug.Assert(prov != null); if(prov == null) throw new ArgumentNullException("prov");
return m_vProviders.Remove(prov);
}
public KeyProvider Get(string strProviderName)
{
if(strProviderName == null) throw new ArgumentNullException("strProviderName");
foreach(KeyProvider prov in m_vProviders)
{
if(prov.Name == strProviderName) return prov;
}
return null;
}
public bool IsKeyProvider(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
foreach(KeyProvider prov in m_vProviders)
{
if(prov.Name == strName) return true;
}
return false;
}
internal byte[] GetKey(string strProviderName, KeyProviderQueryContext ctx,
out bool bPerformHash)
{
Debug.Assert(strProviderName != null); if(strProviderName == null) throw new ArgumentNullException("strProviderName");
bPerformHash = true;
foreach(KeyProvider prov in m_vProviders)
{
if(prov.Name == strProviderName)
{
bPerformHash = !prov.DirectKey;
return prov.GetKey(ctx);
}
}
Debug.Assert(false);
return null;
}
}
}

View File

@@ -0,0 +1,51 @@
/*
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.Text;
namespace KeePassLib.Keys
{
public enum KeyValidationType
{
MasterPassword = 0
}
public abstract class KeyValidator
{
/// <summary>
/// Name of your key validator (should be unique).
/// </summary>
public abstract string Name
{
get;
}
/// <summary>
/// Validate a key.
/// </summary>
/// <param name="strKey">Key to validate.</param>
/// <param name="t">Type of the validation to perform.</param>
/// <returns>Returns <c>null</c>, if the validation is successful.
/// If there's a problem with the key, the returned string describes
/// the problem.</returns>
public abstract string Validate(string strKey, KeyValidationType t);
}
}

View File

@@ -0,0 +1,86 @@
/*
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.Text;
using System.Diagnostics;
using KeePassLib.Utility;
namespace KeePassLib.Keys
{
public sealed class KeyValidatorPool : IEnumerable<KeyValidator>
{
private List<KeyValidator> m_vValidators = new List<KeyValidator>();
public int Count
{
get { return m_vValidators.Count; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vValidators.GetEnumerator();
}
public IEnumerator<KeyValidator> GetEnumerator()
{
return m_vValidators.GetEnumerator();
}
public void Add(KeyValidator v)
{
Debug.Assert(v != null); if(v == null) throw new ArgumentNullException("v");
m_vValidators.Add(v);
}
public bool Remove(KeyValidator v)
{
Debug.Assert(v != null); if(v == null) throw new ArgumentNullException("v");
return m_vValidators.Remove(v);
}
public string Validate(string strKey, KeyValidationType t)
{
Debug.Assert(strKey != null); if(strKey == null) throw new ArgumentNullException("strKey");
foreach(KeyValidator v in m_vValidators)
{
string strResult = v.Validate(strKey, t);
if(strResult != null) return strResult;
}
return null;
}
public string Validate(byte[] pbKeyUtf8, KeyValidationType t)
{
Debug.Assert(pbKeyUtf8 != null); if(pbKeyUtf8 == null) throw new ArgumentNullException("pbKeyUtf8");
if(m_vValidators.Count == 0) return null;
string strKey = StrUtil.Utf8.GetString(pbKeyUtf8, 0, pbKeyUtf8.Length);
return Validate(strKey, t);
}
}
}

View File

@@ -0,0 +1,33 @@
/*
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;
namespace KeePassLib.Keys
{
[Flags]
public enum UserKeyType
{
None = 0,
Other = 1,
Password = 2,
KeyFile = 4,
UserAccount = 8
}
}

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