diff --git a/src/AndroidFileChooserBindingSdkStyle/AndroidFileChooserBindingSdkStyle.csproj b/src/AndroidFileChooserBindingSdkStyle/AndroidFileChooserBindingSdkStyle.csproj
new file mode 100644
index 00000000..c7147177
--- /dev/null
+++ b/src/AndroidFileChooserBindingSdkStyle/AndroidFileChooserBindingSdkStyle.csproj
@@ -0,0 +1,16 @@
+
+
+ net8.0-android
+ 21
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AndroidFileChooserBindingSdkStyle/Transforms/EnumFields.xml b/src/AndroidFileChooserBindingSdkStyle/Transforms/EnumFields.xml
new file mode 100644
index 00000000..22959957
--- /dev/null
+++ b/src/AndroidFileChooserBindingSdkStyle/Transforms/EnumFields.xml
@@ -0,0 +1,14 @@
+
+
+
\ No newline at end of file
diff --git a/src/AndroidFileChooserBindingSdkStyle/Transforms/EnumMethods.xml b/src/AndroidFileChooserBindingSdkStyle/Transforms/EnumMethods.xml
new file mode 100644
index 00000000..49216c61
--- /dev/null
+++ b/src/AndroidFileChooserBindingSdkStyle/Transforms/EnumMethods.xml
@@ -0,0 +1,13 @@
+
+
+
\ No newline at end of file
diff --git a/src/AndroidFileChooserBindingSdkStyle/Transforms/Metadata.xml b/src/AndroidFileChooserBindingSdkStyle/Transforms/Metadata.xml
new file mode 100644
index 00000000..ea974e92
--- /dev/null
+++ b/src/AndroidFileChooserBindingSdkStyle/Transforms/Metadata.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/src/JavaFileStorageBindingsStkStyle/Additions/AboutAdditions.txt b/src/JavaFileStorageBindingsStkStyle/Additions/AboutAdditions.txt
new file mode 100644
index 00000000..2775bd36
--- /dev/null
+++ b/src/JavaFileStorageBindingsStkStyle/Additions/AboutAdditions.txt
@@ -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; }
+}
\ No newline at end of file
diff --git a/src/JavaFileStorageBindingsStkStyle/JavaFileStorageBindingsStkStyle.csproj b/src/JavaFileStorageBindingsStkStyle/JavaFileStorageBindingsStkStyle.csproj
new file mode 100644
index 00000000..e29888f6
--- /dev/null
+++ b/src/JavaFileStorageBindingsStkStyle/JavaFileStorageBindingsStkStyle.csproj
@@ -0,0 +1,38 @@
+
+
+ net8.0-android
+ 21
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/JavaFileStorageBindingsStkStyle/Transforms/EnumFields.xml b/src/JavaFileStorageBindingsStkStyle/Transforms/EnumFields.xml
new file mode 100644
index 00000000..22959957
--- /dev/null
+++ b/src/JavaFileStorageBindingsStkStyle/Transforms/EnumFields.xml
@@ -0,0 +1,14 @@
+
+
+
\ No newline at end of file
diff --git a/src/JavaFileStorageBindingsStkStyle/Transforms/EnumMethods.xml b/src/JavaFileStorageBindingsStkStyle/Transforms/EnumMethods.xml
new file mode 100644
index 00000000..49216c61
--- /dev/null
+++ b/src/JavaFileStorageBindingsStkStyle/Transforms/EnumMethods.xml
@@ -0,0 +1,13 @@
+
+
+
\ No newline at end of file
diff --git a/src/JavaFileStorageBindingsStkStyle/Transforms/Metadata.xml b/src/JavaFileStorageBindingsStkStyle/Transforms/Metadata.xml
new file mode 100644
index 00000000..d1e7bc03
--- /dev/null
+++ b/src/JavaFileStorageBindingsStkStyle/Transforms/Metadata.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JavaFileStorageBindingsStkStyle/dropbox-core-sdk-5.4.6.jar b/src/JavaFileStorageBindingsStkStyle/dropbox-core-sdk-5.4.6.jar
new file mode 100644
index 00000000..9004a26d
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/dropbox-core-sdk-5.4.6.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/commons-logging-1.1.1.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/commons-logging-1.1.1.jar
new file mode 100644
index 00000000..8758a96b
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/commons-logging-1.1.1.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/google-api-client-1.30.5.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/google-api-client-1.30.5.jar
new file mode 100644
index 00000000..73581bab
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/google-api-client-1.30.5.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/google-api-client-android-1.30.5.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/google-api-client-android-1.30.5.jar
new file mode 100644
index 00000000..4f3769ff
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/google-api-client-android-1.30.5.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/google-api-services-drive-v2-rev102-1.16.0-rc.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/google-api-services-drive-v2-rev102-1.16.0-rc.jar
new file mode 100644
index 00000000..5baa6abc
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/google-api-services-drive-v2-rev102-1.16.0-rc.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-1.32.1.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-1.32.1.jar
new file mode 100644
index 00000000..8db57607
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-1.32.1.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-android-1.32.1.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-android-1.32.1.jar
new file mode 100644
index 00000000..da2a9691
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-android-1.32.1.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-gson-1.16.0-rc.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-gson-1.16.0-rc.jar
new file mode 100644
index 00000000..5387880d
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-gson-1.16.0-rc.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-jackson-1.16.0-rc.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-jackson-1.16.0-rc.jar
new file mode 100644
index 00000000..411d2238
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-jackson-1.16.0-rc.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-jackson2-1.32.1.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-jackson2-1.32.1.jar
new file mode 100644
index 00000000..6220777b
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/google-http-client-jackson2-1.32.1.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/google-oauth-client-1.30.4.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/google-oauth-client-1.30.4.jar
new file mode 100644
index 00000000..a7a8690b
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/google-oauth-client-1.30.4.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/grpc-context-1.22.1.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/grpc-context-1.22.1.jar
new file mode 100644
index 00000000..4c7ff849
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/grpc-context-1.22.1.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/httpclient-4.0.3.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/httpclient-4.0.3.jar
new file mode 100644
index 00000000..fd0d3774
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/httpclient-4.0.3.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/httpcore-4.0.1.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/httpcore-4.0.1.jar
new file mode 100644
index 00000000..4638daa5
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/httpcore-4.0.1.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/httpmime-4.0.3.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/httpmime-4.0.3.jar
new file mode 100644
index 00000000..0dfd3312
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/httpmime-4.0.3.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/json_simple-1.1.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/json_simple-1.1.jar
new file mode 100644
index 00000000..f395f414
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/json_simple-1.1.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/jsr305-3.0.2.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/jsr305-3.0.2.jar
new file mode 100644
index 00000000..59222d9c
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/jsr305-3.0.2.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/opencensus-api-0.24.0.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/opencensus-api-0.24.0.jar
new file mode 100644
index 00000000..dcace99b
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/opencensus-api-0.24.0.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gdrive/opencensus-contrib-http-util-0.24.0.jar b/src/JavaFileStorageBindingsStkStyle/gdrive/opencensus-contrib-http-util-0.24.0.jar
new file mode 100644
index 00000000..4f91a71b
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gdrive/opencensus-contrib-http-util-0.24.0.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/gson-2.8.6.jar b/src/JavaFileStorageBindingsStkStyle/gson-2.8.6.jar
new file mode 100644
index 00000000..4765c4af
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/gson-2.8.6.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/jackson-core-2.13.5.jar b/src/JavaFileStorageBindingsStkStyle/jackson-core-2.13.5.jar
new file mode 100644
index 00000000..401dee31
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/jackson-core-2.13.5.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/okhttp-4.12.0.jar b/src/JavaFileStorageBindingsStkStyle/okhttp-4.12.0.jar
new file mode 100644
index 00000000..faf3fa86
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/okhttp-4.12.0.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/okhttp-digest-3.1.0.jar b/src/JavaFileStorageBindingsStkStyle/okhttp-digest-3.1.0.jar
new file mode 100644
index 00000000..af2c08ae
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/okhttp-digest-3.1.0.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/okio-3.6.0.jar b/src/JavaFileStorageBindingsStkStyle/okio-3.6.0.jar
new file mode 100644
index 00000000..f3137562
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/okio-3.6.0.jar differ
diff --git a/src/JavaFileStorageBindingsStkStyle/okio-jvm-3.6.0.jar b/src/JavaFileStorageBindingsStkStyle/okio-jvm-3.6.0.jar
new file mode 100644
index 00000000..ec8ad90f
Binary files /dev/null and b/src/JavaFileStorageBindingsStkStyle/okio-jvm-3.6.0.jar differ
diff --git a/src/KP2AKdbLibraryBindingSdkStyle/KP2AKdbLibraryBindingSdkStyle.csproj b/src/KP2AKdbLibraryBindingSdkStyle/KP2AKdbLibraryBindingSdkStyle.csproj
new file mode 100644
index 00000000..d39c9b67
--- /dev/null
+++ b/src/KP2AKdbLibraryBindingSdkStyle/KP2AKdbLibraryBindingSdkStyle.csproj
@@ -0,0 +1,21 @@
+
+
+ net8.0-android
+ 21
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+ Designer
+
+
+
\ No newline at end of file
diff --git a/src/KP2AKdbLibraryBindingSdkStyle/Transforms/EnumFields.xml b/src/KP2AKdbLibraryBindingSdkStyle/Transforms/EnumFields.xml
new file mode 100644
index 00000000..22959957
--- /dev/null
+++ b/src/KP2AKdbLibraryBindingSdkStyle/Transforms/EnumFields.xml
@@ -0,0 +1,14 @@
+
+
+
\ No newline at end of file
diff --git a/src/KP2AKdbLibraryBindingSdkStyle/Transforms/EnumMethods.xml b/src/KP2AKdbLibraryBindingSdkStyle/Transforms/EnumMethods.xml
new file mode 100644
index 00000000..49216c61
--- /dev/null
+++ b/src/KP2AKdbLibraryBindingSdkStyle/Transforms/EnumMethods.xml
@@ -0,0 +1,13 @@
+
+
+
\ No newline at end of file
diff --git a/src/KP2AKdbLibraryBindingSdkStyle/Transforms/Metadata.xml b/src/KP2AKdbLibraryBindingSdkStyle/Transforms/Metadata.xml
new file mode 100644
index 00000000..0dd04e93
--- /dev/null
+++ b/src/KP2AKdbLibraryBindingSdkStyle/Transforms/Metadata.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/KeePass.sln b/src/KeePass.sln
index 7eade212..8fa56205 100644
--- a/src/KeePass.sln
+++ b/src/KeePass.sln
@@ -29,6 +29,24 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aAutofillParser", "Kp2aA
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aAutofillParserTest", "Kp2aAutofillParserTest\Kp2aAutofillParserTest.csproj", "{3D1560FF-86BB-4CB4-8367-80BA13B81C38}"
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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|x64.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/KeePassLib2Android/KeePassLib2Android.csproj b/src/KeePassLib2Android/KeePassLib2Android.csproj
index 849a5cf5..eb445c9f 100644
--- a/src/KeePassLib2Android/KeePassLib2Android.csproj
+++ b/src/KeePassLib2Android/KeePassLib2Android.csproj
@@ -32,9 +32,10 @@
True
bin\Release
prompt
- 4
+ 5
False
False
+ x86
none
@@ -149,7 +150,6 @@
-
@@ -179,5 +179,8 @@
KP2AKdbLibraryBinding
+
+
+
\ No newline at end of file
diff --git a/src/KeePassLib2Android/Serialization/ProtoBuf/KdbpFile.cs b/src/KeePassLib2Android/Serialization/ProtoBuf/KdbpFile.cs
deleted file mode 100644
index c5b5f669..00000000
--- a/src/KeePassLib2Android/Serialization/ProtoBuf/KdbpFile.cs
+++ /dev/null
@@ -1,1444 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.IO;
-using KeePassLib.Collections;
-using KeePassLib.Cryptography;
-using KeePassLib.Security;
-using KeePassLib.Utility;
-using ProtoBuf;
-using ProtoBuf.Meta;
-
-namespace KeePassLib.Serialization
-{
- public class KdbpFile
- {
- public const string FileNameExtension = "kdbp";
-
- ///
- /// Precompiles the serializer classes for faster first-run execution
- ///
- public static void PrepareSerializer()
- {
- RuntimeTypeModel.Default[typeof(PwDatabaseBuffer)].CompileInPlace();
- }
-
- ///
- /// Determines whether the database pointed to by the specified ioc should be (de)serialised in default (xml) or protocol buffers format.
- ///
- public static KdbxFormat GetFormatToUse(string fileExt)
- {
- return fileExt.Equals(KdbpFile.FileNameExtension, StringComparison.OrdinalIgnoreCase) ? KdbxFormat.ProtocolBuffers :
- (fileExt.Equals("xml", StringComparison.OrdinalIgnoreCase) ? KdbxFormat.PlainXml : KdbxFormat.Default);
- }
-
- public static void WriteDocument(PwDatabase database, Stream stream, byte[] protectedStreamKey, byte[] hashOfHeader)
- {
- var context = new SerializationContext
- {
- Context = new BufferContext(database,
- new CryptoRandomStream(CrsAlgorithm.Salsa20, protectedStreamKey), hashOfHeader)
- };
-
- RuntimeTypeModel.Default.Serialize(stream, new PwDatabaseBuffer(database), context);
- }
-
- public static void ReadDocument(PwDatabase database, Stream stream, byte[] protectedStreamKey, byte[] expectedHashOfHeader)
- {
-
- var context = new BufferContext(database, new CryptoRandomStream(CrsAlgorithm.Salsa20, protectedStreamKey));
-
- // Deserialisation will occur into the database already in context.
- RuntimeTypeModel.Default.Deserialize(stream, null, typeof(PwDatabaseBuffer), new SerializationContext { Context = context });
-
- if (expectedHashOfHeader.Length > 0 &&
- !KeePassLib.Utility.MemUtil.ArraysEqual(context.HeaderHash, expectedHashOfHeader))
- {
- throw new IOException(KeePassLib.Resources.KLRes.FileCorrupted);
- }
- }
-
- private class BufferContext
- {
- // ProtectedBinary objects may be referred to multipe times by entry histories, so reference them only once by ensuring a 1:1 mapping to the buffer wrapping them.
- public readonly Dictionary BinaryPool = new Dictionary();
-
- public readonly PwDatabase Database;
- public readonly CryptoRandomStream RandomStream;
- public byte[] HeaderHash;
-
- public BufferContext(PwDatabase database, CryptoRandomStream randomStream, byte[] pbHashOfHeader = null)
- {
- Database = database;
- RandomStream = randomStream;
- HeaderHash = pbHashOfHeader;
- }
- }
-
- [ProtoContract]
- private class PwDatabaseBuffer
- {
- #region Serialization
-
- private PwDatabase mDatabase;
- private PwDeletedObjectListBuffer mDeletedObjects;
- private PwCustomIconListBuffer mCustomIcons;
-
- public PwDatabaseBuffer(PwDatabase database)
- {
- mDatabase = database;
- mDeletedObjects = new PwDeletedObjectListBuffer(mDatabase.DeletedObjects);
- mCustomIcons = new PwCustomIconListBuffer(mDatabase.CustomIcons);
- }
-
- [ProtoBeforeSerialization]
- private void BeforeSerialization(SerializationContext context)
- {
- var bufferContext = (BufferContext)context.Context;
-
- System.Diagnostics.Debug.Assert(mDatabase == bufferContext.Database);
-
- HeaderHash = bufferContext.HeaderHash;
- }
- #endregion
-
- #region Deserialization
- public PwDatabaseBuffer()
- {
- }
-
- [ProtoBeforeDeserialization]
- private void BeforeDeserialization(SerializationContext context)
- {
- var bufferContext = (BufferContext)context.Context;
-
- mDatabase = bufferContext.Database;
- mDeletedObjects = new PwDeletedObjectListBuffer(mDatabase.DeletedObjects);
- mCustomIcons = new PwCustomIconListBuffer(mDatabase.CustomIcons);
- }
-
- [ProtoAfterDeserialization]
- private void AfterDeserialization(SerializationContext context)
- {
- var bufferContext = (BufferContext)context.Context;
-
- bufferContext.HeaderHash = HeaderHash;
- }
- #endregion
-
- [ProtoMember(1)]
- public string Generator
- {
- get { return PwDatabase.LocalizedAppName; }
- set { /* Ignore */ }
- }
-
- [ProtoMember(2, OverwriteList = true)]
- public byte[] HeaderHash;
-
- [ProtoMember(3)]
- public string Name
- {
- get { return mDatabase.Name; }
- set { mDatabase.Name = value; }
- }
-
- [ProtoMember(4)]
- public DateTime NameChanged
- {
- get { return mDatabase.NameChanged.ToUniversalTime(); }
- set { mDatabase.NameChanged = value.ToLocalTime(); }
- }
-
- [ProtoMember(5)]
- public string Description
- {
- get { return mDatabase.Description; }
- set { mDatabase.Description = value; }
- }
-
- [ProtoMember(6)]
- public DateTime DescriptionChanged
- {
- get { return mDatabase.DescriptionChanged.ToUniversalTime(); }
- set { mDatabase.DescriptionChanged = value.ToLocalTime(); }
- }
-
- [ProtoMember(7)]
- public string DefaultUserName
- {
- get { return mDatabase.DefaultUserName; }
- set { mDatabase.DefaultUserName = value; }
- }
-
- [ProtoMember(8)]
- public DateTime DefaultUserNameChanged
- {
- get { return mDatabase.DefaultUserNameChanged.ToUniversalTime(); }
- set { mDatabase.DefaultUserNameChanged = value.ToLocalTime(); }
- }
-
- [ProtoMember(9)]
- public uint MaintenanceHistoryDays
- {
- get { return mDatabase.MaintenanceHistoryDays; }
- set { mDatabase.MaintenanceHistoryDays = value; }
- }
-
- [ProtoMember(10)]
- public int Color
- {
- get { return mDatabase.Color.ToArgb(); }
- set { mDatabase.Color = System.Drawing.Color.FromArgb(value); }
- }
-
- [ProtoMember(11)]
- public DateTime MasterKeyChanged
- {
- get { return mDatabase.MasterKeyChanged.ToUniversalTime(); }
- set { mDatabase.MasterKeyChanged = value.ToLocalTime(); }
- }
-
- [ProtoMember(12)]
- public long MasterKeyChangeRec
- {
- get { return mDatabase.MasterKeyChangeRec; }
- set { mDatabase.MasterKeyChangeRec = value; }
- }
-
- [ProtoMember(13)]
- public long MasterKeyChangeForce
- {
- get { return mDatabase.MasterKeyChangeForce; }
- set { mDatabase.MasterKeyChangeForce = value; }
- }
-
- [ProtoMember(14)]
- public MemoryProtectionConfigBuffer MemoryProtection
- {
- get { return new MemoryProtectionConfigBuffer(mDatabase.MemoryProtection); }
- set { mDatabase.MemoryProtection = value.MemoryProtectionConfig; }
- }
-
- [ProtoMember(15)]
- public PwCustomIconListBuffer CustomIcons
- {
- get { return mCustomIcons; }
- }
-
- [ProtoMember(16)]
- public bool RecycleBinEnabled
- {
- get { return mDatabase.RecycleBinEnabled; }
- set { mDatabase.RecycleBinEnabled = value; }
- }
-
- [ProtoMember(17, OverwriteList = true)]
- public byte[] RecycleBinUuid
- {
- get { return mDatabase.RecycleBinUuid.UuidBytes; }
- set { mDatabase.RecycleBinUuid = new PwUuid(value); }
- }
-
- [ProtoMember(18)]
- public DateTime RecycleBinChanged
- {
- get { return mDatabase.RecycleBinChanged.ToUniversalTime(); }
- set { mDatabase.RecycleBinChanged = value.ToLocalTime(); }
- }
-
- [ProtoMember(19, OverwriteList = true)]
- public byte[] EntryTemplatesGroup
- {
- get { return mDatabase.EntryTemplatesGroup.UuidBytes; }
- set { mDatabase.EntryTemplatesGroup = new PwUuid(value); }
- }
-
- [ProtoMember(20)]
- public DateTime EntryTemplatesGroupChanged
- {
- get { return mDatabase.EntryTemplatesGroupChanged.ToUniversalTime(); }
- set { mDatabase.EntryTemplatesGroupChanged = value.ToLocalTime(); }
- }
-
- [ProtoMember(21)]
- public int HistoryMaxItems
- {
- get { return mDatabase.HistoryMaxItems; }
- set { mDatabase.HistoryMaxItems = value; }
- }
-
- [ProtoMember(22)]
- public long HistoryMaxSize
- {
- get { return mDatabase.HistoryMaxSize; }
- set { mDatabase.HistoryMaxSize = value; }
- }
-
- [ProtoMember(23, OverwriteList = true)]
- public byte[] LastSelectedGroup
- {
- get { return mDatabase.LastSelectedGroup.UuidBytes; }
- set { mDatabase.LastSelectedGroup = new PwUuid(value); }
- }
-
- [ProtoMember(24, OverwriteList = true)]
- public byte[] LastTopVisibleGroup
- {
- get { return mDatabase.LastTopVisibleGroup.UuidBytes; }
- set { mDatabase.LastTopVisibleGroup = new PwUuid(value); }
- }
-
- [ProtoMember(25)]
- public StringDictionaryExBuffer CustomData
- {
- get { return new StringDictionaryExBuffer(mDatabase.CustomData); }
- set { mDatabase.CustomData = value.StringDictionaryEx; }
- }
-
- [ProtoMember(27)]
- public PwGroupBuffer RootGroup
- {
- get { return new PwGroupBuffer(mDatabase.RootGroup); }
- set { mDatabase.RootGroup = value.Group; }
- }
-
- [ProtoMember(28)]
- public PwDeletedObjectListBuffer DeletedObjects
- {
- get { return mDeletedObjects; }
- }
- }
-
- [ProtoContract]
- private class StringDictionaryExBuffer : IEnumerable>
- {
- #region Serialization
- private StringDictionaryEx mStringDictionaryEx;
-
- public StringDictionaryExBuffer(StringDictionaryEx stringDictionaryEx)
- {
- mStringDictionaryEx = stringDictionaryEx;
- }
- #endregion
-
- #region Deserialization
- public StringDictionaryExBuffer()
- {
- mStringDictionaryEx = new StringDictionaryEx();
- }
-
- public StringDictionaryEx StringDictionaryEx { get { return mStringDictionaryEx; } }
-
- public void Add(KeyValuePair kvp)
- {
- mStringDictionaryEx.Set(kvp.Key, kvp.Value);
- }
- #endregion
-
- public IEnumerator> GetEnumerator()
- {
- return mStringDictionaryEx.GetEnumerator();
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-
- [ProtoContract]
- private class PwCustomIconListBuffer : IEnumerable
- {
- private List mCustomIcons;
- #region Serialization
- public PwCustomIconListBuffer(List customIcons)
- {
- mCustomIcons = customIcons;
- }
- #endregion
-
- #region Deserialization
- public void Add(PwCustomIconBuffer item)
- {
- mCustomIcons.Add(item.CustomIcon);
- }
- #endregion
-
- public IEnumerator GetEnumerator()
- {
- foreach (var customIcon in mCustomIcons)
- {
- yield return new PwCustomIconBuffer(customIcon);
- }
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-
- [ProtoContract]
- private class PwCustomIconBuffer
- {
- #region Serialization
- private PwCustomIcon mCustomIcon;
- public PwCustomIconBuffer(PwCustomIcon CustomIcon)
- {
- mCustomIcon = CustomIcon;
- Uuid = mCustomIcon.Uuid.UuidBytes;
- ImageData = mCustomIcon.ImageDataPng;
- }
- #endregion
-
- #region Deserialization
- public PwCustomIconBuffer()
- {
- }
-
- [ProtoAfterDeserialization]
- private void AfterDeserialization(SerializationContext context)
- {
- mCustomIcon = new PwCustomIcon(new PwUuid(Uuid), ImageData);
- }
-
- public PwCustomIcon CustomIcon { get { return mCustomIcon; } }
- #endregion
-
- [ProtoMember(1, OverwriteList = true)]
- public byte[] Uuid;
-
- [ProtoMember(2, OverwriteList = true)]
- public byte[] ImageData;
- }
-
- [ProtoContract]
- private class MemoryProtectionConfigBuffer
- {
- #region Serialization
- private readonly MemoryProtectionConfig mMemoryProtectionConfig;
-
- public MemoryProtectionConfigBuffer(MemoryProtectionConfig memoryProtectionConfig)
- {
- mMemoryProtectionConfig = memoryProtectionConfig;
- }
- #endregion
-
- #region Deserialization
- public MemoryProtectionConfigBuffer()
- {
- mMemoryProtectionConfig = new MemoryProtectionConfig();
- }
-
- public MemoryProtectionConfig MemoryProtectionConfig { get { return mMemoryProtectionConfig; } }
- #endregion
-
- [ProtoMember(1)]
- public bool ProtectTitle
- {
- get { return mMemoryProtectionConfig.ProtectTitle; }
- set { mMemoryProtectionConfig.ProtectTitle = value; }
- }
-
- [ProtoMember(2)]
- public bool ProtectUserName
- {
- get { return mMemoryProtectionConfig.ProtectUserName; }
- set { mMemoryProtectionConfig.ProtectUserName = value; }
- }
-
- [ProtoMember(3)]
- public bool ProtectPassword
- {
- get { return mMemoryProtectionConfig.ProtectPassword; }
- set { mMemoryProtectionConfig.ProtectPassword = value; }
- }
-
- [ProtoMember(4)]
- public bool ProtectUrl
- {
- get { return mMemoryProtectionConfig.ProtectUrl; }
- set { mMemoryProtectionConfig.ProtectUrl = value; }
- }
-
- [ProtoMember(5)]
- public bool ProtectNotes
- {
- get { return mMemoryProtectionConfig.ProtectNotes; }
- set { mMemoryProtectionConfig.ProtectNotes = value; }
- }
- }
-
- [ProtoContract]
- private class PwDeletedObjectListBuffer : PwObjectListBufferBase
- {
- #region Serialization
- public PwDeletedObjectListBuffer(PwObjectList objectList)
- : base(objectList)
- {
- }
-
- protected override PwDeletedObjectBuffer CreateBuffer(PwDeletedObject item)
- {
- return new PwDeletedObjectBuffer(item);
- }
- #endregion
-
- #region Deserialization
- public PwDeletedObjectListBuffer()
- : base()
- {
- }
-
- public override void Add(PwDeletedObjectBuffer item)
- {
- ObjectList.Add(item.DeletedObject);
- }
- #endregion
- }
-
- [ProtoContract]
- private class PwDeletedObjectBuffer
- {
- #region Serialization
- private readonly PwDeletedObject mDeletedObject;
-
- public PwDeletedObjectBuffer(PwDeletedObject deletedObject)
- {
- mDeletedObject = deletedObject;
- }
- #endregion
-
- #region Deserialization
- public PwDeletedObjectBuffer()
- {
- mDeletedObject = new PwDeletedObject();
- }
-
- public PwDeletedObject DeletedObject { get { return mDeletedObject; } }
- #endregion
-
- [ProtoMember(1, OverwriteList = true)]
- public byte[] Uuid
- {
- get { return mDeletedObject.Uuid.UuidBytes; }
- set { mDeletedObject.Uuid = new PwUuid(value); }
- }
-
- [ProtoMember(2)]
- public DateTime DeletionTime
- {
- get { return mDeletedObject.DeletionTime.ToUniversalTime(); }
- set { mDeletedObject.DeletionTime = value.ToLocalTime(); }
- }
- }
-
- [ProtoContract]
- private class PwGroupBuffer
- {
- #region Serialization
- private readonly PwGroup mGroup;
- private readonly PwGroupEntryListBuffer mEntries;
- private readonly PwGroupGroupListBuffer mGroups;
-
- public PwGroupBuffer(PwGroup group)
- {
- mGroup = group;
- mEntries = new PwGroupEntryListBuffer(mGroup);
- mGroups = new PwGroupGroupListBuffer(mGroup);
- }
- #endregion
-
- #region Deserialization
- public PwGroupBuffer()
- {
- mGroup = new PwGroup(false, false);
- mEntries = new PwGroupEntryListBuffer(mGroup);
- mGroups = new PwGroupGroupListBuffer(mGroup);
- }
-
- public PwGroup Group { get { return mGroup; } }
- #endregion
-
- [ProtoMember(1, OverwriteList = true)]
- public byte[] Uuid
- {
- get { return mGroup.Uuid.UuidBytes; }
- set { mGroup.Uuid = new PwUuid(value); }
- }
-
- [ProtoMember(2)]
- public string Name
- {
- get { return mGroup.Name; }
- set { mGroup.Name = value; }
- }
-
- [ProtoMember(3)]
- public string Notes
- {
- get { return mGroup.Notes; }
- set { mGroup.Notes = value; }
- }
-
- [ProtoMember(4)]
- public PwIcon IconId
- {
- get { return mGroup.IconId; }
- set { mGroup.IconId = value; }
- }
-
- [ProtoMember(5, OverwriteList = true)]
- public byte[] CustomIconUuid
- {
- get { return mGroup.CustomIconUuid.Equals(PwUuid.Zero) ? null : mGroup.CustomIconUuid.UuidBytes; ; }
- set { mGroup.CustomIconUuid = value == null ? PwUuid.Zero : new PwUuid(value); }
- }
-
- [ProtoMember(6)]
- public bool IsExpanded
- {
- get { return mGroup.IsExpanded; }
- set { mGroup.IsExpanded = value; }
- }
-
- [ProtoMember(7)]
- public string DefaultAutoTypeSequence
- {
- get { return mGroup.DefaultAutoTypeSequence; }
- set { mGroup.DefaultAutoTypeSequence = value; }
- }
-
- [ProtoMember(8)]
- public DateTime LastModificationTime
- {
- get { return mGroup.LastModificationTime.ToUniversalTime(); }
- set { mGroup.LastModificationTime = value.ToLocalTime(); }
- }
-
- [ProtoMember(9)]
- public DateTime CreationTime
- {
- get { return mGroup.CreationTime.ToUniversalTime(); }
- set { mGroup.CreationTime = value.ToLocalTime(); }
- }
-
- [ProtoMember(10)]
- public DateTime LastAccessTime
- {
- get { return mGroup.LastAccessTime.ToUniversalTime(); }
- set { mGroup.LastAccessTime = value.ToLocalTime(); }
- }
-
- [ProtoMember(11)]
- public DateTime ExpiryTime
- {
- get { return mGroup.ExpiryTime.ToUniversalTime(); }
- set { mGroup.ExpiryTime = value.ToLocalTime(); }
- }
-
- [ProtoMember(12)]
- public bool Expires
- {
- get { return mGroup.Expires; }
- set { mGroup.Expires = value; }
- }
-
- [ProtoMember(13)]
- public ulong UsageCount
- {
- get { return mGroup.UsageCount; }
- set { mGroup.UsageCount = value; }
- }
-
- [ProtoMember(14)]
- public DateTime LocationChanged
- {
- get { return mGroup.LocationChanged.ToUniversalTime(); }
- set { mGroup.LocationChanged = value.ToLocalTime(); }
- }
-
- [ProtoMember(15)]
- public bool? EnableAutoType
- {
- get { return mGroup.EnableAutoType; }
- set { mGroup.EnableAutoType = value; }
- }
-
- [ProtoMember(16)]
- public bool? EnableSearching
- {
- get { return mGroup.EnableSearching; }
- set { mGroup.EnableSearching = value; }
- }
-
- [ProtoMember(17, OverwriteList = true)]
- public byte[] LastTopVisibleEntry
- {
- get { return mGroup.LastTopVisibleEntry.UuidBytes; }
- set { mGroup.LastTopVisibleEntry = new PwUuid(value); }
- }
-
- [ProtoMember(18)]
- public PwGroupGroupListBuffer Groups
- {
- get { return mGroups; }
- }
-
- [ProtoMember(19)]
- public PwGroupEntryListBuffer Entries
- {
- get { return mEntries; }
- }
- }
-
- private abstract class PwObjectListBufferBase : IEnumerable
- where TData : class, KeePassLib.Interfaces.IDeepCloneable
- {
- #region Serialization
- private PwObjectList mObjectList;
-
- protected PwObjectListBufferBase(PwObjectList objectList)
- {
- mObjectList = objectList;
- }
-
- protected abstract TDataBuffer CreateBuffer(TData item);
- #endregion
-
- #region Deserialization
- protected PwObjectListBufferBase()
- {
- mObjectList = new PwObjectList();
- }
-
- public PwObjectList ObjectList { get { return mObjectList; } }
-
- public abstract void Add(TDataBuffer item);
- #endregion
-
- public IEnumerator GetEnumerator()
- {
- foreach (var item in mObjectList)
- {
- yield return CreateBuffer(item);
- }
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-
- [ProtoContract]
- private class PwGroupGroupListBuffer : PwObjectListBufferBase
- {
- #region Serialization
- private PwGroup mGroup;
- public PwGroupGroupListBuffer(PwGroup group)
- : base(group.Groups)
- {
- mGroup = group;
- }
-
- protected override PwGroupBuffer CreateBuffer(PwGroup item)
- {
- return new PwGroupBuffer(item);
- }
- #endregion
-
- #region Deserialization
- public override void Add(PwGroupBuffer item)
- {
- mGroup.AddGroup(item.Group, true);
- }
- #endregion
- }
-
- [ProtoContract]
- private class PwGroupEntryListBuffer : PwObjectListBufferBase
- {
- #region Serialization
- private PwGroup mGroup;
- public PwGroupEntryListBuffer(PwGroup group)
- : base(group.Entries)
- {
- mGroup = group;
- }
-
- protected override PwEntryBuffer CreateBuffer(PwEntry item)
- {
- return new PwEntryBuffer(item);
- }
- #endregion
-
- #region Deserialization
- public override void Add(PwEntryBuffer item)
- {
- mGroup.AddEntry(item.Entry, true);
- }
- #endregion
- }
-
- [ProtoContract]
- private class PwEntryListBuffer : PwObjectListBufferBase
- {
- #region Serialization
- public PwEntryListBuffer(PwObjectList entryList)
- : base(entryList)
- {
- }
-
- protected override PwEntryBuffer CreateBuffer(PwEntry item)
- {
- return new PwEntryBuffer(item);
- }
- #endregion
-
- #region Deserialization
- public PwEntryListBuffer()
- : base()
- {
- }
-
- public override void Add(PwEntryBuffer item)
- {
- ObjectList.Add(item.Entry);
- }
- #endregion
- }
-
- [ProtoContract]
- private class PwEntryBuffer
- {
- #region Serialization
- private readonly PwEntry mEntry;
- private ProtectedStandardFieldDictionaryBuffer mEntryStandardStrings;
- private ProtectedCustomFieldDictionaryBuffer mEntryCustomStrings;
- private NamedProtectedBinaryListBuffer mEntryBinaries;
-
- public PwEntryBuffer(PwEntry entry)
- {
- mEntry = entry;
- }
-
- [ProtoBeforeSerialization]
- private void BeforeSerialization(SerializationContext context)
- {
- var bufferContext = (BufferContext)context.Context;
-
- // ProtectedStringDictionaryBuffer nver gets its own ProtoBeforeSerialization called as it's a list of objects rather than an object itself
- List> customFields;
- mEntryStandardStrings = new ProtectedStandardFieldDictionaryBuffer(mEntry.Strings, (int)mEntry.Strings.UCount, bufferContext, out customFields);
- mEntryCustomStrings = new ProtectedCustomFieldDictionaryBuffer(customFields);
- mEntryBinaries = new NamedProtectedBinaryListBuffer(mEntry.Binaries, (int)mEntry.Binaries.UCount, bufferContext);
- }
- #endregion
-
- #region Deserialization
- public PwEntryBuffer()
- {
- mEntry = new PwEntry(false, false);
- mEntryStandardStrings = new ProtectedStandardFieldDictionaryBuffer(mEntry.Strings);
- mEntryCustomStrings = new ProtectedCustomFieldDictionaryBuffer(mEntry.Strings);
- mEntryBinaries = new NamedProtectedBinaryListBuffer(mEntry.Binaries);
- }
-
- public PwEntry Entry { get { return mEntry; } }
- #endregion
-
- [ProtoMember(1, OverwriteList = true)]
- public byte[] Uuid
- {
- get { return mEntry.Uuid.UuidBytes; }
- set { mEntry.SetUuid(new PwUuid(value), false); }
- }
-
- [ProtoMember(2)]
- public PwIcon IconId
- {
- get { return mEntry.IconId; }
- set { mEntry.IconId = value; }
- }
-
- [ProtoMember(3, OverwriteList = true)]
- public byte[] CustomIconUuid
- {
- get { return mEntry.CustomIconUuid.Equals(PwUuid.Zero) ? null : mEntry.CustomIconUuid.UuidBytes; }
- set { mEntry.CustomIconUuid = value == null ? PwUuid.Zero : new PwUuid(value); }
- }
-
- [ProtoMember(4)]
- public int ForegroundColor
- {
- get { return mEntry.ForegroundColor.ToArgb(); }
- set { mEntry.ForegroundColor = Color.FromArgb(value); }
- }
-
- [ProtoMember(5)]
- public int BackgroundColor
- {
- get { return mEntry.BackgroundColor.ToArgb(); }
- set { mEntry.BackgroundColor = Color.FromArgb(value); }
- }
-
- [ProtoMember(6)]
- public string OverrideUrl
- {
- get { return mEntry.OverrideUrl; }
- set { mEntry.OverrideUrl = value; }
- }
-
- [ProtoMember(7)]
- public IList Tags
- {
- get { return mEntry.Tags; }
- }
-
- [ProtoMember(8)]
- public DateTime LastModificationTime
- {
- get { return mEntry.LastModificationTime.ToUniversalTime(); }
- set { mEntry.LastModificationTime = value.ToLocalTime(); }
- }
-
- [ProtoMember(9)]
- public DateTime CreationTime
- {
- get { return mEntry.CreationTime.ToUniversalTime(); }
- set { mEntry.CreationTime = value.ToLocalTime(); }
- }
-
- [ProtoMember(10)]
- public DateTime LastAccessTime
- {
- get { return mEntry.LastAccessTime.ToUniversalTime(); }
- set { mEntry.LastAccessTime = value.ToLocalTime(); }
- }
-
- [ProtoMember(11)]
- public DateTime ExpiryTime
- {
- get { return mEntry.ExpiryTime.ToUniversalTime(); }
- set { mEntry.ExpiryTime = value.ToLocalTime(); }
- }
-
- [ProtoMember(12)]
- public bool Expires
- {
- get { return mEntry.Expires; }
- set { mEntry.Expires = value; }
- }
-
- [ProtoMember(13)]
- public ulong UsageCount
- {
- get { return mEntry.UsageCount; }
- set { mEntry.UsageCount = value; }
- }
-
- [ProtoMember(14)]
- public DateTime LocationChanged
- {
- get { return mEntry.LocationChanged.ToUniversalTime(); }
- set { mEntry.LocationChanged = value.ToLocalTime(); }
- }
-
- [ProtoMember(15)]
- public ProtectedStandardFieldDictionaryBuffer StandardStrings
- {
- get { return mEntryStandardStrings; }
- }
-
- [ProtoMember(16)]
- public ProtectedCustomFieldDictionaryBuffer CustomStrings
- {
- get { return mEntryCustomStrings; }
- }
-
- [ProtoMember(17, AsReference = true)]
- public NamedProtectedBinaryListBuffer Binaries
- {
- get { return mEntryBinaries; }
- }
-
- [ProtoMember(18)]
- public AutoTypeConfigBuffer AutoType
- {
- get { return new AutoTypeConfigBuffer(mEntry.AutoType); }
- set { mEntry.AutoType = value.AutoTypeConfig; }
- }
-
- [ProtoMember(19)]
- public PwEntryListBuffer History
- {
- get { return new PwEntryListBuffer(mEntry.History); }
- set { mEntry.History = value.ObjectList; }
- }
-
- }
-
- private abstract class ProtectedStringDictionaryBuffer : IEnumerable>
- {
- #region Serialization
- private List> mProtectedStringBuffers;
-
- ///
- /// Serialisation constructor. Reads strings from dictionary, does not write to it
- ///
- protected ProtectedStringDictionaryBuffer(int capacity)
- {
- mProtectedStringBuffers = new List>(capacity);
- }
-
- protected void AddStringField(TKey key, ProtectedString value, bool? overrideProtect)
- {
- mProtectedStringBuffers.Add(new KeyValuePair(key, new ProtectedStringBuffer(value, overrideProtect)));
- }
-
- public IEnumerator> GetEnumerator()
- {
- return mProtectedStringBuffers.GetEnumerator();
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- #endregion
-
- #region Deserialization
- private readonly ProtectedStringDictionary mDictionary;
-
- ///
- /// Deerialisation constructor. Writes strings to dictionary, does read from it
- ///
- protected ProtectedStringDictionaryBuffer(ProtectedStringDictionary dictionary)
- {
- mDictionary = dictionary;
- }
-
- public void Add(KeyValuePair item)
- {
- mDictionary.Set(GetFieldName(item.Key), item.Value.ProtectedString);
- }
-
- protected abstract string GetFieldName(TKey key);
-
- #endregion
- }
-
- [ProtoContract]
- private class ProtectedCustomFieldDictionaryBuffer : ProtectedStringDictionaryBuffer
- {
- public ProtectedCustomFieldDictionaryBuffer(List> entryStrings)
- : base(entryStrings.Count)
- {
- foreach (var kvp in entryStrings)
- {
- System.Diagnostics.Debug.Assert(!PwDefs.IsStandardField(kvp.Key));
- AddStringField(kvp.Key, kvp.Value, null);
- }
- }
-
- public ProtectedCustomFieldDictionaryBuffer(ProtectedStringDictionary dictionary)
- : base(dictionary)
- { }
-
- protected override string GetFieldName(string key)
- {
- return key;
- }
- }
-
- [ProtoContract]
- private class ProtectedStandardFieldDictionaryBuffer : ProtectedStringDictionaryBuffer
- {
- public enum StandardField
- {
- Title,
- UserName,
- Password,
- Url,
- Notes
- }
-
- public ProtectedStandardFieldDictionaryBuffer(IEnumerable> entryStrings, int entryStringCount, BufferContext context,
- out List> customFields) // Perf optimisation - return the custom fields so we don't have to determine them again
- : base(entryStringCount)
- {
- customFields = new List>(entryStringCount);
-
- var database = context.Database;
-
- foreach (var kvp in entryStrings)
- {
- var field = GetField(kvp.Key);
-
- if (field.HasValue)
- {
- // Logic from KdbxFile.Write
-
- bool? overrideProtect = null;
- // Adjust memory protection setting (which might be different
- // from the database default, e.g. due to an import which
- // didn't specify the correct setting)
- switch (field.Value)
- {
- case StandardField.Title:
- overrideProtect = database.MemoryProtection.ProtectTitle;
- break;
- case StandardField.UserName:
- overrideProtect = database.MemoryProtection.ProtectUserName;
- break;
- case StandardField.Password:
- overrideProtect = database.MemoryProtection.ProtectPassword;
- break;
- case StandardField.Url:
- overrideProtect = database.MemoryProtection.ProtectUrl;
- break;
- case StandardField.Notes:
- overrideProtect = database.MemoryProtection.ProtectNotes;
- break;
- }
-
- AddStringField(field.Value, kvp.Value, overrideProtect);
- }
- else
- {
- customFields.Add(kvp);
- }
- }
- }
-
- private static StandardField? GetField(string fieldName)
- {
- switch (fieldName)
- {
- case PwDefs.TitleField:
- return StandardField.Title;
- case PwDefs.UserNameField:
- return StandardField.UserName;
- case PwDefs.PasswordField:
- return StandardField.Password;
- case PwDefs.UrlField:
- return StandardField.Url;
- case PwDefs.NotesField:
- return StandardField.Notes;
-
- default:
- System.Diagnostics.Debug.Assert(!PwDefs.IsStandardField(fieldName));
- return null;
- }
- }
-
- public ProtectedStandardFieldDictionaryBuffer(ProtectedStringDictionary dictionary)
- : base(dictionary)
- { }
-
- protected override string GetFieldName(StandardField key)
- {
- switch (key)
- {
- case StandardField.Title:
- return PwDefs.TitleField;
- case StandardField.UserName:
- return PwDefs.UserNameField;
- case StandardField.Password:
- return PwDefs.PasswordField;
- case StandardField.Url:
- return PwDefs.UrlField;
- case StandardField.Notes:
- return PwDefs.NotesField;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- }
-
- [ProtoContract]
- private class ProtectedStringBuffer
- {
- #region Serialisation
- private ProtectedString mProtectedString;
-
- public ProtectedStringBuffer(ProtectedString protectedString, bool? overrideProtect)
- {
- mProtectedString = protectedString;
- IsProtected = overrideProtect.GetValueOrDefault(mProtectedString.IsProtected);
- }
-
- [ProtoBeforeSerialization]
- private void BeforeSerialization(SerializationContext context)
- {
- if (IsProtected)
- {
- Value = mProtectedString.ReadXorredString(((BufferContext)context.Context).RandomStream);
- }
- else
- {
- Value = mProtectedString.ReadUtf8();
- }
- }
- #endregion
-
- #region Deserialisation
- public ProtectedStringBuffer()
- {
- }
-
- [ProtoAfterDeserialization]
- private void AfterDeserialization(SerializationContext context)
- {
- if (IsProtected)
- {
- byte[] pbPad = ((BufferContext)context.Context).RandomStream.GetRandomBytes((uint)Value.Length);
- mProtectedString = new ProtectedString(IsProtected, new XorredBuffer(Value, pbPad));
- }
- else
- {
- mProtectedString = new ProtectedString(IsProtected, Value);
- }
- }
-
- public ProtectedString ProtectedString { get { return mProtectedString; } }
-
- #endregion
-
- [ProtoMember(1)]
- public bool IsProtected;
-
- [ProtoMember(2, OverwriteList = true)]
- public byte[] Value;
- }
-
- [ProtoContract]
- private class NamedProtectedBinaryListBuffer : IEnumerable
- {
- #region Serialisation
- private readonly List mNamedBinaries;
-
- public NamedProtectedBinaryListBuffer(IEnumerable> binaries, int binariesCount, BufferContext context)
- {
- mNamedBinaries = new List(binariesCount);
- foreach (var kvp in binaries)
- {
- NamedProtectedBinaryBuffer namedProtectedBinaryBuffer;
- if (!context.BinaryPool.TryGetValue(kvp.Value, out namedProtectedBinaryBuffer))
- {
- // Hasn't been put in the pool yet, so create it
- namedProtectedBinaryBuffer = new NamedProtectedBinaryBuffer(kvp);
- context.BinaryPool.Add(kvp.Value, namedProtectedBinaryBuffer);
- }
- mNamedBinaries.Add(namedProtectedBinaryBuffer);
- }
- }
-
- public IEnumerator GetEnumerator()
- {
- return mNamedBinaries.GetEnumerator();
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- #endregion
-
- #region Deserialization
- private readonly ProtectedBinaryDictionary mBinaryDictionary;
-
- public NamedProtectedBinaryListBuffer(ProtectedBinaryDictionary binaryDictionary)
- {
- mBinaryDictionary = binaryDictionary;
- }
-
- public void Add(NamedProtectedBinaryBuffer item)
- {
- mBinaryDictionary.Set(item.Name, item.ProtectedBinary);
- }
- #endregion
- }
-
- [ProtoContract]
- private class NamedProtectedBinaryBuffer
- {
- #region Serialization
- private ProtectedBinary mProtectedBinary;
- public NamedProtectedBinaryBuffer(KeyValuePair namedBinary)
- {
- Name = namedBinary.Key;
- mProtectedBinary = namedBinary.Value;
- IsProtected = mProtectedBinary.IsProtected;
- }
-
- [ProtoBeforeSerialization]
- private void BeforeSerialization(SerializationContext context)
- {
- if (IsProtected)
- {
- Value = mProtectedBinary.ReadXorredData(((BufferContext)context.Context).RandomStream);
- }
- else
- {
- Value = mProtectedBinary.ReadData();
- }
- }
- #endregion
-
- #region Deserialisation
- public NamedProtectedBinaryBuffer()
- {
- }
-
- [ProtoAfterDeserialization]
- private void AfterDeserialization(SerializationContext context)
- {
- if (IsProtected)
- {
- byte[] pbPad = ((BufferContext)context.Context).RandomStream.GetRandomBytes((uint)Value.Length);
- mProtectedBinary = new ProtectedBinary(IsProtected, new XorredBuffer(Value, pbPad));
- }
- else
- {
- mProtectedBinary = new ProtectedBinary(IsProtected, Value);
- }
- }
-
- public ProtectedBinary ProtectedBinary { get { return mProtectedBinary; } }
-
- #endregion
-
- [ProtoMember(1)]
- public string Name;
-
- [ProtoMember(2)]
- public bool IsProtected;
-
- [ProtoMember(3, OverwriteList = true)]
- public byte[] Value;
- }
-
- [ProtoContract]
- private class AutoTypeConfigBuffer
- {
- private readonly AutoTypeAssociationsBuffer mAutoTypeAssociationsBuffer;
- #region Serialization
- private AutoTypeConfig mAutoTypeConfig;
- public AutoTypeConfigBuffer(AutoTypeConfig autoTypeConfig)
- {
- mAutoTypeConfig = autoTypeConfig;
- mAutoTypeAssociationsBuffer = new AutoTypeAssociationsBuffer(mAutoTypeConfig);
- }
- #endregion
-
- #region Deserialization
- public AutoTypeConfigBuffer()
- {
- mAutoTypeConfig = new AutoTypeConfig();
- mAutoTypeAssociationsBuffer = new AutoTypeAssociationsBuffer(mAutoTypeConfig);
- }
-
- public AutoTypeConfig AutoTypeConfig { get { return mAutoTypeConfig; } }
- #endregion
-
- [ProtoMember(1)]
- public bool Enabled
- {
- get { return mAutoTypeConfig.Enabled; }
- set { mAutoTypeConfig.Enabled = value; }
- }
-
- [ProtoMember(2)]
- public AutoTypeObfuscationOptions ObfuscationOptions
- {
- get { return mAutoTypeConfig.ObfuscationOptions; }
- set { mAutoTypeConfig.ObfuscationOptions = value; }
- }
-
- [ProtoMember(3)]
- public string DefaultSequence
- {
- get { return mAutoTypeConfig.DefaultSequence; }
- set { mAutoTypeConfig.DefaultSequence = value; }
- }
-
- [ProtoMember(4)]
- public AutoTypeAssociationsBuffer Associations
- {
- get { return mAutoTypeAssociationsBuffer; }
- }
- }
-
- [ProtoContract]
- private class AutoTypeAssociationsBuffer : IEnumerable
- {
- #region Serialization
- private AutoTypeConfig mAutoTypeConfig;
-
- public AutoTypeAssociationsBuffer(AutoTypeConfig autoTypeConfig)
- {
- mAutoTypeConfig = autoTypeConfig;
- }
-
- public IEnumerator GetEnumerator()
- {
- foreach (var autoTypeAssociation in mAutoTypeConfig.Associations)
- {
- yield return new AutoTypeAssociationBuffer(autoTypeAssociation);
- }
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- #endregion
-
- #region Deserialization
- public void Add(AutoTypeAssociationBuffer value)
- {
- mAutoTypeConfig.Add(value.AutoTypeAssociation);
- }
- #endregion
- }
-
- [ProtoContract]
- private class AutoTypeAssociationBuffer
- {
- #region Serialization
- private AutoTypeAssociation mAutoTypeAssociation;
-
- public AutoTypeAssociationBuffer(AutoTypeAssociation autoTypeAssociation)
- {
- mAutoTypeAssociation = autoTypeAssociation;
- }
- #endregion
-
- #region Deserialization
- public AutoTypeAssociationBuffer()
- {
- mAutoTypeAssociation = new AutoTypeAssociation();
- }
-
- public AutoTypeAssociation AutoTypeAssociation { get { return mAutoTypeAssociation; } }
- #endregion
-
- [ProtoMember(1)]
- public string WindowName
- {
- get { return mAutoTypeAssociation.WindowName; }
- set { mAutoTypeAssociation.WindowName = value; }
- }
-
- [ProtoMember(2)]
- public string Sequence
- {
- get { return mAutoTypeAssociation.Sequence; }
- set { mAutoTypeAssociation.Sequence = value; }
- }
- }
- }
-}
diff --git a/src/KeePassLib2Android/Utility/UrlUtil.cs b/src/KeePassLib2Android/Utility/UrlUtil.cs
index cca71920..e6122114 100644
--- a/src/KeePassLib2Android/Utility/UrlUtil.cs
+++ b/src/KeePassLib2Android/Utility/UrlUtil.cs
@@ -687,25 +687,6 @@ namespace KeePassLib.Utility
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
// Structurally mostly equivalent to UrlUtil.GetFileInfos
diff --git a/src/KeePassLib2AndroidSdkStyle/Collections/AutoTypeConfig.cs b/src/KeePassLib2AndroidSdkStyle/Collections/AutoTypeConfig.cs
new file mode 100644
index 00000000..dd2d5a28
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Collections/AutoTypeConfig.cs
@@ -0,0 +1,244 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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,
+ IDeepCloneable
+ {
+ 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();
+ }
+ }
+
+ ///
+ /// A list of auto-type associations.
+ ///
+ public sealed class AutoTypeConfig : IEquatable,
+ IDeepCloneable
+ {
+ private bool m_bEnabled = true;
+ private AutoTypeObfuscationOptions m_atooObfuscation =
+ AutoTypeObfuscationOptions.None;
+ private string m_strDefaultSequence = string.Empty;
+ private List m_lWindowAssocs =
+ new List();
+
+ ///
+ /// Specify whether auto-type is enabled or not.
+ ///
+ public bool Enabled
+ {
+ get { return m_bEnabled; }
+ set { m_bEnabled = value; }
+ }
+
+ ///
+ /// Specify whether the typing should be obfuscated.
+ ///
+ public AutoTypeObfuscationOptions ObfuscationOptions
+ {
+ get { return m_atooObfuscation; }
+ set { m_atooObfuscation = value; }
+ }
+
+ ///
+ /// The default keystroke sequence that is auto-typed if
+ /// no matching window is found in the Associations
+ /// container.
+ ///
+ public string DefaultSequence
+ {
+ get { return m_strDefaultSequence; }
+ set
+ {
+ Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
+ m_strDefaultSequence = value;
+ }
+ }
+
+ ///
+ /// Get all auto-type window/keystroke sequence pairs.
+ ///
+ public IEnumerable Associations
+ {
+ get { return m_lWindowAssocs; }
+ }
+
+ public int AssociationsCount
+ {
+ get { return m_lWindowAssocs.Count; }
+ }
+
+ ///
+ /// Construct a new auto-type associations list.
+ ///
+ public AutoTypeConfig()
+ {
+ }
+
+ ///
+ /// Remove all associations.
+ ///
+ public void Clear()
+ {
+ m_lWindowAssocs.Clear();
+ }
+
+ ///
+ /// Clone the auto-type associations list.
+ ///
+ /// New, cloned object.
+ 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);
+ // }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Collections/ProtectedBinaryDictionary.cs b/src/KeePassLib2AndroidSdkStyle/Collections/ProtectedBinaryDictionary.cs
new file mode 100644
index 00000000..beaf2c52
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Collections/ProtectedBinaryDictionary.cs
@@ -0,0 +1,178 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// A list of ProtectedBinary objects (dictionary).
+ ///
+ public sealed class ProtectedBinaryDictionary :
+ IDeepCloneable,
+ IEnumerable>
+ {
+ /*
+ private SortedDictionary m_vBinaries =
+ new SortedDictionary();
+ */
+
+ private Dictionary m_vBinaries =
+ new Dictionary();
+
+
+ ///
+ /// Get the number of binaries in this entry.
+ ///
+ public uint UCount
+ {
+ get { return (uint)m_vBinaries.Count; }
+ }
+
+ ///
+ /// Construct a new list of protected binaries.
+ ///
+ public ProtectedBinaryDictionary()
+ {
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return m_vBinaries.GetEnumerator();
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return m_vBinaries.GetEnumerator();
+ }
+
+ public void Clear()
+ {
+ m_vBinaries.Clear();
+ }
+
+ ///
+ /// Clone the current ProtectedBinaryList object, including all
+ /// stored protected strings.
+ ///
+ /// New ProtectedBinaryList object.
+ public ProtectedBinaryDictionary CloneDeep()
+ {
+ ProtectedBinaryDictionary plNew = new ProtectedBinaryDictionary();
+
+ foreach(KeyValuePair 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 kvp in m_vBinaries)
+ {
+ ProtectedBinary pb = dict.Get(kvp.Key);
+ if(pb == null) return false;
+ if(!pb.Equals(kvp.Value)) return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Get one of the stored binaries.
+ ///
+ /// Binary identifier.
+ /// Protected binary. If the binary identified by
+ /// cannot be found, the function
+ /// returns null.
+ /// Thrown if the input
+ /// parameter is null.
+ 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;
+ }
+
+ ///
+ /// Set a binary object.
+ ///
+ /// Identifier of the binary field to modify.
+ /// New value. This parameter must not be null.
+ /// Thrown if any of the input
+ /// parameters is null.
+ 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;
+ }
+
+ ///
+ /// Remove a binary object.
+ ///
+ /// Identifier of the binary field to remove.
+ /// Returns true if the object has been successfully
+ /// removed, otherwise false.
+ /// Thrown if the input parameter
+ /// is null.
+ 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 kvp in m_vBinaries)
+ {
+ if(sb.Length > 0) sb.Append(", ");
+ sb.Append(kvp.Key);
+ }
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Collections/ProtectedBinarySet.cs b/src/KeePassLib2AndroidSdkStyle/Collections/ProtectedBinarySet.cs
new file mode 100644
index 00000000..786d6af0
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Collections/ProtectedBinarySet.cs
@@ -0,0 +1,170 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2021 Dominik Reichl
+
+ 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>
+ {
+ private Dictionary m_d =
+ new Dictionary();
+
+ private readonly bool m_bDedupAdd;
+
+ public ProtectedBinarySet(bool bDedupAdd)
+ {
+ m_bDedupAdd = bDedupAdd;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return m_d.GetEnumerator();
+ }
+
+ public IEnumerator> 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 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 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 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 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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Collections/ProtectedStringDictionary.cs b/src/KeePassLib2AndroidSdkStyle/Collections/ProtectedStringDictionary.cs
new file mode 100644
index 00000000..b93068e0
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Collections/ProtectedStringDictionary.cs
@@ -0,0 +1,304 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// A list of ProtectedString objects (dictionary).
+ ///
+ public sealed class ProtectedStringDictionary :
+ IDeepCloneable,
+ IEnumerable>
+ {
+ /*private SortedDictionary m_vStrings =
+ new SortedDictionary();*/
+ private Dictionary m_vStrings = new Dictionary();
+
+ ///
+ /// Get the number of strings in this entry.
+ ///
+ public uint UCount
+ {
+ get { return (uint)m_vStrings.Count; }
+ }
+
+ ///
+ /// Construct a new list of protected strings.
+ ///
+ public ProtectedStringDictionary()
+ {
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return m_vStrings.GetEnumerator();
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return m_vStrings.GetEnumerator();
+ }
+
+ public void Clear()
+ {
+ m_vStrings.Clear();
+ }
+
+ ///
+ /// Clone the current ProtectedStringList object, including all
+ /// stored protected strings.
+ ///
+ /// New ProtectedStringList object.
+ public ProtectedStringDictionary CloneDeep()
+ {
+ ProtectedStringDictionary plNew = new ProtectedStringDictionary();
+
+ foreach(KeyValuePair 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 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 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;
+ }
+
+ ///
+ /// Get one of the protected strings.
+ ///
+ /// String identifier.
+ /// Protected string. If the string identified by
+ /// cannot be found, the function
+ /// returns null.
+ /// Thrown if the input parameter
+ /// is null.
+ 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;
+ }
+
+ ///
+ /// Get one of the protected strings. The return value is never null.
+ /// If the requested string cannot be found, an empty protected string
+ /// object is returned.
+ ///
+ /// String identifier.
+ /// Returns a protected string object. If the standard string
+ /// has not been set yet, the return value is an empty string ("").
+ /// Thrown if the input
+ /// parameter is null.
+ 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;
+ }
+
+ ///
+ /// Test if a named string exists.
+ ///
+ /// Name of the string to try.
+ /// Returns true if the string exists, otherwise false.
+ /// Thrown if
+ /// is null.
+ public bool Exists(string strName)
+ {
+ Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
+
+ return m_vStrings.ContainsKey(strName);
+ }
+
+ ///
+ /// Get one of the protected strings. If the string doesn't exist, the
+ /// return value is an empty string ("").
+ ///
+ /// Name of the requested string.
+ /// Requested string value or an empty string, if the named
+ /// string doesn't exist.
+ /// Thrown if the input
+ /// parameter is null.
+ 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;
+ }
+
+ ///
+ /// Get one of the entry strings. If the string doesn't exist, the
+ /// return value is an empty string (""). If the string is
+ /// in-memory protected, the return value is PwDefs.HiddenPassword.
+ ///
+ /// Name of the requested string.
+ /// Returns the requested string in plain-text or
+ /// PwDefs.HiddenPassword if the string cannot be found.
+ /// Thrown if the input
+ /// parameter is null.
+ 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;
+ }
+
+ ///
+ /// Set a string.
+ ///
+ /// Identifier of the string field to modify.
+ /// New value. This parameter must not be null.
+ /// Thrown if one of the input
+ /// parameters is null.
+ 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;
+ }
+
+ ///
+ /// Delete a string.
+ ///
+ /// Name of the string field to delete.
+ /// Returns true if the field has been successfully
+ /// removed, otherwise the return value is false.
+ /// Thrown if the input
+ /// parameter is null.
+ public bool Remove(string strField)
+ {
+ Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
+
+ return m_vStrings.Remove(strField);
+ }
+
+ public List GetKeys()
+ {
+ return new List(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);
+ }
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Collections/PwObjectList.cs b/src/KeePassLib2AndroidSdkStyle/Collections/PwObjectList.cs
new file mode 100644
index 00000000..bde863b7
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Collections/PwObjectList.cs
@@ -0,0 +1,373 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// List of objects that implement IDeepCloneable,
+ /// and cannot be null.
+ ///
+ /// Type specifier.
+ public sealed class PwObjectList : IEnumerable
+ where T : class, IDeepCloneable
+ {
+ private List m_vObjects = new List();
+
+ ///
+ /// Get number of objects in this list.
+ ///
+ public uint UCount
+ {
+ get { return (uint)m_vObjects.Count; }
+ }
+
+ ///
+ /// Construct a new list of objects.
+ ///
+ public PwObjectList()
+ {
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return m_vObjects.GetEnumerator();
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return m_vObjects.GetEnumerator();
+ }
+
+ public void Clear()
+ {
+ // Do not destroy contained objects!
+ m_vObjects.Clear();
+ }
+
+ ///
+ /// Clone the current PwObjectList, including all
+ /// stored objects (deep copy).
+ ///
+ /// New PwObjectList.
+ public PwObjectList CloneDeep()
+ {
+ PwObjectList pl = new PwObjectList();
+
+ foreach(T po in m_vObjects)
+ pl.Add(po.CloneDeep());
+
+ return pl;
+ }
+
+ public PwObjectList CloneShallow()
+ {
+ PwObjectList tNew = new PwObjectList();
+
+ foreach(T po in m_vObjects) tNew.Add(po);
+
+ return tNew;
+ }
+
+ public List CloneShallowToList()
+ {
+ PwObjectList tNew = CloneShallow();
+ return tNew.m_vObjects;
+ }
+
+ ///
+ /// Add an object to this list.
+ ///
+ /// Object to be added.
+ /// Thrown if the input
+ /// parameter is null.
+ public void Add(T pwObject)
+ {
+ Debug.Assert(pwObject != null);
+ if(pwObject == null) throw new ArgumentNullException("pwObject");
+
+ m_vObjects.Add(pwObject);
+ }
+
+ public void Add(PwObjectList 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 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);
+ }
+
+ ///
+ /// Get an object of the list.
+ ///
+ /// Index of the object to get. Must be valid, otherwise an
+ /// exception is thrown.
+ /// Reference to an existing T object. Is never null.
+ 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;
+ }
+
+ ///
+ /// Get a range of objects.
+ ///
+ /// Index of the first object to be
+ /// returned (inclusive).
+ /// Index of the last object to be
+ /// returned (inclusive).
+ ///
+ public List 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 list = new List((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);
+ }
+
+ ///
+ /// Delete an object of this list. The object to be deleted is identified
+ /// by a reference handle.
+ ///
+ /// Reference of the object to be deleted.
+ /// Returns true if the object was deleted, false if
+ /// the object wasn't found in this list.
+ /// Thrown if the input
+ /// parameter is null.
+ 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);
+ }
+
+ ///
+ /// Move an object up or down.
+ ///
+ /// The object to be moved.
+ /// Move one up. If false, move one down.
+ 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");
+ ///
+ List lIndices = new List();
+ 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); }
+ }
+ ///
+ MoveOne(lIndices.ToArray(), bUp);
+ }
+ /// List of objects to be moved.
+ public void MoveOne(int[] vIndices, bool bUp)
+ {
+ Debug.Assert(vIndices != null);
+ if(vIndices == null) throw new ArgumentNullException("vIndices");
+ /// Move to top. If false, move to bottom.
+ 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(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);
+ }
+ }
+ }
+
+ ///
+ /// Move some of the objects in this list to the top/bottom.
+ ///
+ /// List of objects to be moved.
+ /// Move to top. If false, move to bottom.
+ 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 tComparer)
+ {
+ if(tComparer == null) throw new ArgumentNullException("tComparer");
+
+ m_vObjects.Sort(tComparer);
+ }
+
+ public static PwObjectList FromArray(T[] tArray)
+ {
+ if(tArray == null) throw new ArgumentNullException("tArray");
+
+ PwObjectList l = new PwObjectList();
+ foreach(T t in tArray) { l.Add(t); }
+ return l;
+ }
+
+ public static PwObjectList FromList(List tList)
+ {
+ if(tList == null) throw new ArgumentNullException("tList");
+
+ PwObjectList l = new PwObjectList();
+ l.Add(tList);
+ return l;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Collections/PwObjectPool.cs b/src/KeePassLib2AndroidSdkStyle/Collections/PwObjectPool.cs
new file mode 100644
index 00000000..55ae6777
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Collections/PwObjectPool.cs
@@ -0,0 +1,232 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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 m_dict =
+ new SortedDictionary();
+
+ 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 kvp in m_dict)
+ {
+ if(kvp.Value.GetType() != t) return false;
+ }
+
+ return true;
+ }
+ }
+
+ internal sealed class PwObjectPoolEx
+ {
+ private Dictionary m_dUuidToId =
+ new Dictionary();
+ private Dictionary m_dIdToItem =
+ new Dictionary();
+
+ 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 : IEnumerable
+ where T : class, ITimeLogger, IStructureItem, IDeepCloneable
+ {
+ private List m_l = new List();
+
+ 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 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;
+ }
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Collections/StringDictionaryEx.cs b/src/KeePassLib2AndroidSdkStyle/Collections/StringDictionaryEx.cs
new file mode 100644
index 00000000..e3c389f9
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Collections/StringDictionaryEx.cs
@@ -0,0 +1,170 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2021 Dominik Reichl
+
+ 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,
+ IEnumerable>, IEquatable
+ {
+ private SortedDictionary m_d =
+ new SortedDictionary();
+
+ // Non-null if and only if last mod. times should be remembered
+ private Dictionary m_dLastMod = null;
+
+ public int Count
+ {
+ get { return m_d.Count; }
+ }
+
+ public StringDictionaryEx()
+ {
+ }
+
+ internal StringDictionaryEx(bool bRememberLastMod)
+ {
+ if (bRememberLastMod) m_dLastMod = new Dictionary();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return m_d.GetEnumerator();
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return m_d.GetEnumerator();
+ }
+
+ public StringDictionaryEx CloneDeep()
+ {
+ StringDictionaryEx sdNew = new StringDictionaryEx();
+
+ foreach (KeyValuePair kvp in m_d)
+ sdNew.m_d[kvp.Key] = kvp.Value;
+
+ if (m_dLastMod != null)
+ sdNew.m_dLastMod = new Dictionary(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 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 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);
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Collections/VariantDictionary.cs b/src/KeePassLib2AndroidSdkStyle/Collections/VariantDictionary.cs
new file mode 100644
index 00000000..509ec312
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Collections/VariantDictionary.cs
@@ -0,0 +1,415 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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 m_d = new Dictionary();
+
+ 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(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(string strName, T t)
+ where T : struct
+ {
+ if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
+
+#if DEBUG
+ T tEx;
+ Get(strName, out tEx); // Assert same type
+#endif
+
+ m_d[strName] = t;
+ }
+
+ private void SetRef(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(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 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(strName, out u)) return u;
+ return uDefault;
+ }
+
+ public void SetUInt32(string strName, uint uValue)
+ {
+ SetStruct(strName, uValue);
+ }
+
+ public ulong GetUInt64(string strName, ulong uDefault)
+ {
+ ulong u;
+ if(Get(strName, out u)) return u;
+ return uDefault;
+ }
+
+ public void SetUInt64(string strName, ulong uValue)
+ {
+ SetStruct(strName, uValue);
+ }
+
+ public bool GetBool(string strName, bool bDefault)
+ {
+ bool b;
+ if(Get(strName, out b)) return b;
+ return bDefault;
+ }
+
+ public void SetBool(string strName, bool bValue)
+ {
+ SetStruct(strName, bValue);
+ }
+
+ public int GetInt32(string strName, int iDefault)
+ {
+ int i;
+ if(Get(strName, out i)) return i;
+ return iDefault;
+ }
+
+ public void SetInt32(string strName, int iValue)
+ {
+ SetStruct(strName, iValue);
+ }
+
+ public long GetInt64(string strName, long lDefault)
+ {
+ long l;
+ if(Get(strName, out l)) return l;
+ return lDefault;
+ }
+
+ public void SetInt64(string strName, long lValue)
+ {
+ SetStruct(strName, lValue);
+ }
+
+ public string GetString(string strName)
+ {
+ string str;
+ Get(strName, out str);
+ return str;
+ }
+
+ public void SetString(string strName, string strValue)
+ {
+ SetRef(strName, strValue);
+ }
+
+ public byte[] GetByteArray(string strName)
+ {
+ byte[] pb;
+ Get(strName, out pb);
+ return pb;
+ }
+
+ public void SetByteArray(string strName, byte[] pbValue)
+ {
+ SetRef(strName, pbValue);
+ }
+
+ ///
+ /// Create a deep copy.
+ ///
+ public virtual object Clone()
+ {
+ VariantDictionary vdNew = new VariantDictionary();
+
+ foreach(KeyValuePair 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 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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/ChaCha20Cipher.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/ChaCha20Cipher.cs
new file mode 100644
index 00000000..540b6409
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/ChaCha20Cipher.cs
@@ -0,0 +1,254 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// Implementation of the ChaCha20 cipher with a 96-bit nonce,
+ /// as specified in RFC 7539.
+ /// https://tools.ietf.org/html/rfc7539
+ ///
+ 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)
+ {
+ }
+
+ ///
+ /// Constructor.
+ ///
+ /// Key (32 bytes).
+ /// Nonce (12 bytes).
+ /// If false, 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 is true, 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
+ /// 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).
+ 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(m_s);
+ MemUtil.ZeroArray(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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/ChaCha20Engine.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/ChaCha20Engine.cs
new file mode 100644
index 00000000..22cee5db
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/ChaCha20Engine.cs
@@ -0,0 +1,191 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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);
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/CipherPool.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/CipherPool.cs
new file mode 100644
index 00000000..e8e25868
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/CipherPool.cs
@@ -0,0 +1,171 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// Pool of encryption/decryption algorithms (ciphers).
+ ///
+ public sealed class CipherPool
+ {
+ private List m_vCiphers = new List();
+ private static CipherPool m_poolGlobal = null;
+
+ ///
+ /// Reference to the global cipher pool.
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Remove all cipher engines from the current pool.
+ ///
+ public void Clear()
+ {
+ m_vCiphers.Clear();
+ }
+
+ ///
+ /// Add a cipher engine to the pool.
+ ///
+ /// Cipher engine to add. Must not be null.
+ 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);
+ }
+
+ ///
+ /// Get a cipher identified by its UUID.
+ ///
+ /// UUID of the cipher to return.
+ /// Reference to the requested cipher. If the cipher is
+ /// not found, null is returned.
+ public ICipherEngine GetCipher(PwUuid uuidCipher)
+ {
+ foreach(ICipherEngine iEngine in m_vCiphers)
+ {
+ if(iEngine.CipherUuid.Equals(uuidCipher))
+ return iEngine;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Get the index of a cipher. This index is temporary and should
+ /// not be stored or used to identify a cipher.
+ ///
+ /// UUID of the cipher.
+ /// Index of the requested cipher. Returns -1 if
+ /// the specified cipher is not found.
+ 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;
+ }
+
+ ///
+ /// Get the index of a cipher. This index is temporary and should
+ /// not be stored or used to identify a cipher.
+ ///
+ /// Name of the cipher. Note that
+ /// multiple ciphers can have the same name. In this case, the
+ /// first matching cipher is returned.
+ /// Cipher with the specified name or -1 if
+ /// no cipher with that name is found.
+ 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;
+ }
+
+ ///
+ /// Get the number of cipher engines in this pool.
+ ///
+ public int EngineCount
+ {
+ get { return m_vCiphers.Count; }
+ }
+
+ ///
+ /// 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.
+ ///
+ /// Index of the requested cipher engine.
+ /// Reference to the cipher engine at the specified
+ /// position.
+ public ICipherEngine this[int nIndex]
+ {
+ get
+ {
+ if((nIndex < 0) || (nIndex >= m_vCiphers.Count))
+ throw new ArgumentOutOfRangeException("nIndex");
+
+ return m_vCiphers[nIndex];
+ }
+ }
+
+ public IEnumerable Engines
+ {
+ get {
+ return m_vCiphers;
+ }
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/CtrBlockCipher.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/CtrBlockCipher.cs
new file mode 100644
index 00000000..a143306c
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/CtrBlockCipher.cs
@@ -0,0 +1,104 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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);
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/ICipherEngine.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/ICipherEngine.cs
new file mode 100644
index 00000000..a5cb652d
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/ICipherEngine.cs
@@ -0,0 +1,87 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// Interface of an encryption/decryption class.
+ ///
+ public interface ICipherEngine
+ {
+ ///
+ /// UUID of the engine. If you want to write an engine/plugin,
+ /// please contact the KeePass team to obtain a new UUID.
+ ///
+ PwUuid CipherUuid
+ {
+ get;
+ }
+
+ ///
+ /// String displayed in the list of available encryption/decryption
+ /// engines in the GUI.
+ ///
+ string DisplayName
+ {
+ get;
+ }
+
+ ///
+ /// Encrypt a stream.
+ ///
+ /// Stream to read the plain-text from.
+ /// Key to use.
+ /// Initialization vector.
+ /// Stream, from which the encrypted data can be read.
+ Stream EncryptStream(Stream sPlainText, byte[] pbKey, byte[] pbIV);
+
+ ///
+ /// Decrypt a stream.
+ ///
+ /// Stream to read the encrypted data from.
+ /// Key to use.
+ /// Initialization vector.
+ /// Stream, from which the decrypted data can be read.
+ Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV);
+ }
+
+ public interface ICipherEngine2 : ICipherEngine
+ {
+ ///
+ /// Length of an encryption key in bytes.
+ /// The base ICipherEngine assumes 32.
+ ///
+ int KeyLength
+ {
+ get;
+}
+
+ ///
+ /// Length of the initialization vector in bytes.
+ /// The base ICipherEngine assumes 16.
+ ///
+ int IVLength
+ {
+ get;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/Salsa20Cipher.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/Salsa20Cipher.cs
new file mode 100644
index 00000000..a0385f20
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/Salsa20Cipher.cs
@@ -0,0 +1,165 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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(m_s);
+ MemUtil.ZeroArray(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];
+ }
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/StandardAesEngine.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/StandardAesEngine.cs
new file mode 100644
index 00000000..8e0c07be
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/Cipher/StandardAesEngine.cs
@@ -0,0 +1,157 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// Standard AES cipher implementation.
+ ///
+ 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;
+
+ ///
+ /// UUID of the cipher engine. This ID uniquely identifies the
+ /// AES engine. Must not be used by other ciphers.
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Get the UUID of this cipher engine as PwUuid object.
+ ///
+ public PwUuid CipherUuid
+ {
+ get { return StandardAesEngine.AesUuid; }
+ }
+
+ ///
+ /// Get a displayable name describing this cipher engine.
+ ///
+ 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);
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/CryptoRandom.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/CryptoRandom.cs
new file mode 100644
index 00000000..312c71be
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/CryptoRandom.cs
@@ -0,0 +1,393 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2016 Dominik Reichl
+
+ 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
+{
+ ///
+ /// Cryptographically secure pseudo-random number generator.
+ /// The returned values are unpredictable and cannot be reproduced.
+ /// CryptoRandom is a singleton class.
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// 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 GetRandomBytes method.
+ ///
+ public ulong GeneratedBytesCount
+ {
+ get
+ {
+ ulong u;
+ lock(m_oSyncRoot) { u = m_uGeneratedBytesCount; }
+ return u;
+ }
+ }
+
+ ///
+ /// Event that is triggered whenever the internal GenerateRandom256
+ /// method is called to generate random bytes.
+ ///
+ 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());
+ }
+
+ ///
+ /// Update the internal seed of the random number generator based
+ /// on entropy data.
+ /// This method is thread-safe.
+ ///
+ /// Entropy bytes.
+ 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;
+ }
+
+ ///
+ /// Get a number of cryptographically strong random bytes.
+ /// This method is thread-safe.
+ ///
+ /// Number of requested random bytes.
+ /// A byte array consisting of
+ /// random bytes.
+ 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);
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/CryptoRandomStream.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/CryptoRandomStream.cs
new file mode 100644
index 00000000..b0081c4b
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/CryptoRandomStream.cs
@@ -0,0 +1,258 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// Algorithms supported by CryptoRandomStream.
+ ///
+ public enum CrsAlgorithm
+ {
+ ///
+ /// Not supported.
+ ///
+ Null = 0,
+
+ ///
+ /// A variant of the ARCFour algorithm (RC4 incompatible).
+ ///
+ ///
+ ArcFourVariant = 1,
+
+ ///
+ /// Salsa20 stream cipher algorithm.
+ ///
+ Salsa20 = 2,
+
+ ///
+ /// ChaCha20 stream cipher algorithm.
+ ///
+ ChaCha20 = 3,
+
+ Count = 4
+ }
+
+ ///
+ /// 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.
+ ///
+ 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;
+
+ ///
+ /// Construct a new cryptographically secure random stream object.
+ ///
+ /// Algorithm to use.
+ /// Initialization key. Must not be null and
+ /// must contain at least 1 byte.
+ public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
+ {
+ if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
+ /// Thrown if the
+ int cbKey = pbKey.Length;
+ if(cbKey <= 0)
+ {
+ Debug.Assert(false); // Need at least one byte
+ throw new ArgumentOutOfRangeException("pbKey");
+ }
+ /// parameter is null.
+ m_crsAlgorithm = a;
+ /// Thrown if the
+ if(a == CrsAlgorithm.ChaCha20)
+ {
+ byte[] pbKey32 = new byte[32];
+ byte[] pbIV12 = new byte[12];
+ /// 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.
+ 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); }
+ }
+ }
+
+ ///
+ /// Get random bytes.
+ ///
+ /// Number of random bytes to retrieve.
+ /// Returns random bytes.
+ 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
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/CryptoUtil.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/CryptoUtil.cs
new file mode 100644
index 00000000..6401f0f8
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/CryptoUtil.cs
@@ -0,0 +1,254 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2020 Dominik Reichl
+
+ 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;
+ }
+
+ ///
+ /// Create a cryptographic key of length
+ /// (in bytes) from .
+ ///
+ 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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/Hash/Blake2b.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/Hash/Blake2b.cs
new file mode 100644
index 00000000..98aaa37e
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/Hash/Blake2b.cs
@@ -0,0 +1,232 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/HashingStreamEx.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/HashingStreamEx.cs
new file mode 100644
index 00000000..c36b9d9d
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/HashingStreamEx.cs
@@ -0,0 +1,184 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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);
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/HmacOtp.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/HmacOtp.cs
new file mode 100644
index 00000000..d401e83f
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/HmacOtp.cs
@@ -0,0 +1,92 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// Generate HMAC-based one-time passwords as specified in RFC 4226.
+ ///
+ 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
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/AesKdf.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/AesKdf.cs
new file mode 100644
index 00000000..cbd2cc56
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/AesKdf.cs
@@ -0,0 +1,281 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/Argon2Kdf.Core.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/Argon2Kdf.Core.cs
new file mode 100644
index 00000000..bf1dc7a7
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/Argon2Kdf.Core.cs
@@ -0,0 +1,682 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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()
+#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(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(pbR);
+ MemUtil.ZeroArray(pbTmp);
+ if (pbAddrInputZero != null) MemUtil.ZeroArray(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(pqBlockHash);
+ MemUtil.ZeroByteArray(pbBlockHashBytes);
+ return pbOut;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/Argon2Kdf.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/Argon2Kdf.cs
new file mode 100644
index 00000000..7b0d1183
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/Argon2Kdf.cs
@@ -0,0 +1,226 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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);
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/KdfEngine.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/KdfEngine.cs
new file mode 100644
index 00000000..7e4f5b5c
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/KdfEngine.cs
@@ -0,0 +1,144 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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);
+ }
+
+ ///
+ /// Generate random seeds and store them in .
+ ///
+ 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);
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/KdfParameters.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/KdfParameters.cs
new file mode 100644
index 00000000..600c1f5c
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/KdfParameters.cs
@@ -0,0 +1,80 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2020 Dominik Reichl
+
+ 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);
+ }
+
+ ///
+ /// Unsupported.
+ ///
+ 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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/KdfPool.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/KdfPool.cs
new file mode 100644
index 00000000..8f8bfa4a
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/KeyDerivation/KdfPool.cs
@@ -0,0 +1,97 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2020 Dominik Reichl
+
+ 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 g_l = new List();
+
+ public static IEnumerable 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);
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/CharSetBasedGenerator.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/CharSetBasedGenerator.cs
new file mode 100644
index 00000000..f7cec856
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/CharSetBasedGenerator.cs
@@ -0,0 +1,65 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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(vGenerated);
+ return PwgError.TooFewCharacters;
+ }
+
+ vGenerated[nIndex] = ch;
+ }
+
+ byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vGenerated);
+ psOut = new ProtectedString(true, pbUtf8);
+ MemUtil.ZeroByteArray(pbUtf8);
+ MemUtil.ZeroArray(vGenerated);
+
+ return PwgError.Success;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/CustomPwGenerator.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/CustomPwGenerator.cs
new file mode 100644
index 00000000..943bcf47
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/CustomPwGenerator.cs
@@ -0,0 +1,66 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+ {
+ ///
+ /// Each custom password generation algorithm must have
+ /// its own unique UUID.
+ ///
+ public abstract PwUuid Uuid { get; }
+
+ ///
+ /// Displayable name of the password generation algorithm.
+ ///
+ public abstract string Name { get; }
+
+ public virtual bool SupportsOptions
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Password generation function.
+ ///
+ /// Password generation options chosen
+ /// by the user. This may be null, if the default
+ /// options should be used.
+ /// Source that the algorithm
+ /// can use to generate random numbers.
+ /// Generated password or null in case
+ /// of failure. If returning null, the caller assumes
+ /// that an error message has already been shown to the user.
+ public abstract ProtectedString Generate(PwProfile prf,
+ CryptoRandomStream crsRandomSource);
+
+ public virtual string GetOptions(string strCurrentOptions)
+ {
+ return string.Empty;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/CustomPwGeneratorPool.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/CustomPwGeneratorPool.cs
new file mode 100644
index 00000000..b0ae5d14
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/CustomPwGeneratorPool.cs
@@ -0,0 +1,110 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+ {
+ private List m_vGens = new List();
+
+ public int Count
+ {
+ get { return m_vGens.Count; }
+ }
+
+ public CustomPwGeneratorPool()
+ {
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return m_vGens.GetEnumerator();
+ }
+
+ public IEnumerator 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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PatternBasedGenerator.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PatternBasedGenerator.cs
new file mode 100644
index 00000000..bb7e7302
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PatternBasedGenerator.cs
@@ -0,0 +1,173 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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 vGenerated = new LinkedList();
+ 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(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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PwCharSet.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PwCharSet.cs
new file mode 100644
index 00000000..97f34248
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PwCharSet.cs
@@ -0,0 +1,351 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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 m_vChars = new List();
+ 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;
+ }
+ }
+
+ ///
+ /// Create a new, empty character set collection object.
+ ///
+ 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();
+ }
+ }
+
+ ///
+ /// Number of characters in this set.
+ ///
+ public uint Size
+ {
+ get { return (uint)m_vChars.Count; }
+ }
+
+ ///
+ /// Get a character of the set using an index.
+ ///
+ /// Index of the character to get.
+ /// Character at the specified position. If the index is invalid,
+ /// an ArgumentOutOfRangeException is thrown.
+ public char this[uint uPos]
+ {
+ get
+ {
+ if(uPos >= (uint)m_vChars.Count)
+ throw new ArgumentOutOfRangeException("uPos");
+
+ return m_vChars[(int)uPos];
+ }
+ }
+
+ ///
+ /// Remove all characters from this set.
+ ///
+ 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;
+ }
+
+ ///
+ /// Add characters to the set.
+ ///
+ /// Character to add.
+ 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));
+ }
+ }
+
+ ///
+ /// Add characters to the set.
+ ///
+ /// String containing characters to add.
+ 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);
+ }
+
+ ///
+ /// Convert the character set to a string containing all its characters.
+ ///
+ /// String containing all character set characters.
+ 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);
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PwGenerator.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PwGenerator.cs
new file mode 100644
index 00000000..73f9284c
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PwGenerator.cs
@@ -0,0 +1,152 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2016 Dominik Reichl
+
+ 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
+ }
+
+ ///
+ /// Utility functions for generating random passwords.
+ ///
+ 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;
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PwProfile.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PwProfile.cs
new file mode 100644
index 00000000..90c99b2b
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/PasswordGenerator/PwProfile.cs
@@ -0,0 +1,278 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// Type of the password generator. Different types like generators
+ /// based on given patterns, based on character sets, etc. are
+ /// available.
+ ///
+ public enum PasswordGeneratorType
+ {
+ ///
+ /// Generator based on character spaces/sets, i.e. groups
+ /// of characters like lower-case, upper-case or numeric characters.
+ ///
+ CharSet = 0,
+
+ ///
+ /// Password generation based on a pattern. The user has provided
+ /// a pattern, which describes how the generated password has to
+ /// look like.
+ ///
+ Pattern = 1,
+
+ Custom = 2
+ }
+
+ public sealed class PwProfile : IDeepCloneable
+ {
+ 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(vChars);
+ MemUtil.ZeroByteArray(pbUtf8);
+ return pp;
+ }
+
+ public bool HasSecurityReducingOption()
+ {
+ return (m_bNoLookAlike || m_bNoRepeat || (m_strExclude.Length > 0));
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/PopularPasswords.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/PopularPasswords.cs
new file mode 100644
index 00000000..6083520d
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/PopularPasswords.cs
@@ -0,0 +1,134 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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> m_dicts =
+ new Dictionary>();
+
+ 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 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 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 d;
+ if(!m_dicts.TryGetValue(cc, out d))
+ {
+ d = new Dictionary();
+ m_dicts[cc] = d;
+ }
+
+ d[strWord] = true;
+ sb.Remove(0, cc);
+ }
+ }
+ else sb.Append(char.ToLower(ch));
+ }
+ }
+ catch(Exception) { Debug.Assert(false); }
+ }
+ }
+}
diff --git a/src/KeePassLib2AndroidSdkStyle/Cryptography/QualityEstimation.cs b/src/KeePassLib2AndroidSdkStyle/Cryptography/QualityEstimation.cs
new file mode 100644
index 00000000..cf835825
--- /dev/null
+++ b/src/KeePassLib2AndroidSdkStyle/Cryptography/QualityEstimation.cs
@@ -0,0 +1,768 @@
+/*
+ KeePass Password Safe - The Open-Source Password Manager
+ Copyright (C) 2003-2017 Dominik Reichl
+
+ 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
+{
+ ///
+ /// A class that offers static functions to estimate the quality of
+ /// passwords.
+ ///
+ 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 m_dHisto = new Dictionary();
+ 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 d = new Dictionary();
+ 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 m_dEncs =
+ new Dictionary();
+
+ 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 Path;
+
+ public QePathState(int iPosition, List lPath)
+ {
+ this.Position = iPosition;
+ this.Path = lPath;
+ }
+ }
+
+ private static object m_objSyncInit = new object();
+ private static List 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();
+
+ 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));
+ }
+ }
+ }
+
+ ///
+ /// Estimate the quality of a password.
+ ///
+ /// Password to check.
+ /// Estimated bit-strength of the password.
+ 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[] vPatterns = new List[n];
+ for(int i = 0; i < n; ++i)
+ {
+ vPatterns[i] = new List();
+
+ 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 sRec = new Stack();
+ sRec.Push(new QePathState(0, new List()));
+ 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 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 lNewPath =
+ new List(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);
+ }
+
+ ///
+ /// Estimate the quality of a password.
+ ///
+ /// Password to check, UTF-8 encoded.
+ /// Estimated bit-strength of the password.
+ 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(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 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[] 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