Compare commits
	
		
			41 Commits
		
	
	
		
			v1.12-r7
			...
			security/a
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 62c361feb0 | ||
|   | 0ee2495528 | ||
|   | 7dc635a625 | ||
|   | e15112c3b4 | ||
|   | 89a09ea142 | ||
|   | 628c0d2c19 | ||
|   | 584feabe44 | ||
|   | ae2cfde897 | ||
|   | 42c66670b8 | ||
|   | 288539b902 | ||
|   | 61fd32f121 | ||
|   | 43108ec4a6 | ||
|   | 51089c6b98 | ||
|   | cf18fcf91c | ||
|   | da0513c768 | ||
|   | 37f520cdbe | ||
|   | c98572bee0 | ||
|   | b1774ffc4b | ||
|   | 57aaa0c4cd | ||
|   | b961ae1b33 | ||
|   | 5e418e2b1b | ||
|   | 6d22a213f3 | ||
|   | a76addc43f | ||
|   | 1d96217713 | ||
|   | d2b8fdcfff | ||
|   | 507b671448 | ||
|   | 3118ffaeb5 | ||
|   | 0abe29bd77 | ||
|   | f3a7831390 | ||
|   | 37cd58f7ba | ||
|   | 7dd80a8ef7 | ||
|   | c78636264b | ||
|   | 035506a5a3 | ||
|   | 4cf46ef062 | ||
|   | c7b8063171 | ||
|   | 0a8b149c9a | ||
|   | 9240a27791 | ||
|   | e90d5b903c | ||
|   | 50b4a9f1b9 | ||
|   | 0aa78ffd66 | ||
|   | c1dbf171f5 | 
							
								
								
									
										29
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -78,7 +78,7 @@ jobs: | |||||||
|  |  | ||||||
|   #   - name: Build keepass2android (net) |   #   - name: Build keepass2android (net) | ||||||
|   #     run: | |   #     run: | | ||||||
|   #       make msbuild Flavor=Net |   #       make dotnetbuild Flavor=Net | ||||||
|  |  | ||||||
|   #   - name: Build APK (net) |   #   - name: Build APK (net) | ||||||
|   #     run: | |   #     run: | | ||||||
| @@ -96,7 +96,7 @@ jobs: | |||||||
|  |  | ||||||
|   #   - name: Build keepass2android (nonet) |   #   - name: Build keepass2android (nonet) | ||||||
|   #     run: | |   #     run: | | ||||||
|   #       make msbuild Flavor=NoNet |   #       make dotnetbuild Flavor=NoNet | ||||||
|  |  | ||||||
|   #   - name: Build APK (nonet) |   #   - name: Build APK (nonet) | ||||||
|   #     run: | |   #     run: | | ||||||
| @@ -212,7 +212,7 @@ jobs: | |||||||
|  |  | ||||||
|   #   - name: Build keepass2android (net) |   #   - name: Build keepass2android (net) | ||||||
|   #     run: | |   #     run: | | ||||||
|   #       make msbuild Flavor=Net |   #       make dotnetbuild Flavor=Net | ||||||
|  |  | ||||||
|   #   - name: Build APK (net) |   #   - name: Build APK (net) | ||||||
|   #     run: | |   #     run: | | ||||||
| @@ -230,7 +230,7 @@ jobs: | |||||||
|  |  | ||||||
|   #   - name: Build keepass2android (nonet) |   #   - name: Build keepass2android (nonet) | ||||||
|   #     run: | |   #     run: | | ||||||
|   #       make msbuild Flavor=NoNet |   #       make dotnetbuild Flavor=NoNet | ||||||
|  |  | ||||||
|   #   - name: Build APK (nonet) |   #   - name: Build APK (nonet) | ||||||
|   #     run: | |   #     run: | | ||||||
| @@ -279,7 +279,7 @@ jobs: | |||||||
|       with: |       with: | ||||||
|         minimum-size: 8GB |         minimum-size: 8GB | ||||||
|  |  | ||||||
|     - name: Add msbuild to PATH |     - name: Add dotnetbuild to PATH | ||||||
|       uses: microsoft/setup-msbuild@v2 |       uses: microsoft/setup-msbuild@v2 | ||||||
|       # If we want to also have nmake, use this instead |       # If we want to also have nmake, use this instead | ||||||
|       #uses: ilammy/msvc-dev-cmd@v1 |       #uses: ilammy/msvc-dev-cmd@v1 | ||||||
| @@ -309,12 +309,20 @@ jobs: | |||||||
|       run: | |       run: | | ||||||
|         make java |         make java | ||||||
|  |  | ||||||
|  |     - name: Update dotnet workloads | ||||||
|  |       run: | | ||||||
|  |         dotnet workload update         | ||||||
|  |  | ||||||
|  |     - name: Select the manifest | ||||||
|  |       run: | | ||||||
|  |         make manifestlink Flavor=Net | ||||||
|  |  | ||||||
|     - name: Install NuGet dependencies (net) |     - name: Install NuGet dependencies (net) | ||||||
|       run: make nuget Flavor=Net |       run: make nuget Flavor=Net | ||||||
|  |  | ||||||
|     - name: Build keepass2android (net) |     - name: Build keepass2android (net) | ||||||
|       run: | |       run: | | ||||||
|         make msbuild Flavor=Net |         make dotnetbuild Flavor=Net | ||||||
|  |  | ||||||
|     - name: Build APK (net) |     - name: Build APK (net) | ||||||
|       run: | |       run: | | ||||||
| @@ -327,12 +335,17 @@ jobs: | |||||||
|         path: | |         path: | | ||||||
|           src/keepass2android/bin/*/*-Signed.apk |           src/keepass2android/bin/*/*-Signed.apk | ||||||
|  |  | ||||||
|  |     - name: Select the manifest | ||||||
|  |       run: | | ||||||
|  |         make manifestlink Flavor=NoNet | ||||||
|  |  | ||||||
|     - name: Install NuGet dependencies (nonet) |     - name: Install NuGet dependencies (nonet) | ||||||
|       run: make nuget Flavor=NoNet |       run: make nuget Flavor=NoNet | ||||||
|  |  | ||||||
|     - name: Build keepass2android (nonet) |     - name: Build keepass2android (nonet) | ||||||
|       run: | |       run: | | ||||||
|         make msbuild Flavor=NoNet |         make dotnetbuild Flavor=NoNet | ||||||
|  |  | ||||||
|     - name: Test Autofill |     - name: Test Autofill | ||||||
|       working-directory: ./src/Kp2aAutofillParser.Tests |       working-directory: ./src/Kp2aAutofillParser.Tests | ||||||
|       run: dotnet test |       run: dotnet test | ||||||
| @@ -348,5 +361,3 @@ jobs: | |||||||
|         path: | |         path: | | ||||||
|           src/keepass2android/bin/*/*-Signed.apk |           src/keepass2android/bin/*/*-Signed.apk | ||||||
|  |  | ||||||
|     - name: Perform "make distclean" |  | ||||||
|       run: make distclean |  | ||||||
|   | |||||||
| @@ -1,19 +1,21 @@ | |||||||
| name: Create keepass2android release | name: Create keepass2android release | ||||||
| env: | env: | ||||||
|   NAME: 'ReleaseNet' |   NAME: 'Release' | ||||||
| 
 | 
 | ||||||
| on: | on: | ||||||
|   release: |   push: | ||||||
|     types: [published]  # Trigger when a new GitHub release is published |     tags: | ||||||
|   workflow_dispatch: # For manual testing |       - "v1.*" | ||||||
| 
 |  | ||||||
| jobs: | jobs: | ||||||
| 
 | 
 | ||||||
|   build-release: |   build-release: | ||||||
| 
 | 
 | ||||||
|     runs-on: windows-2022 |     runs-on: windows-2022 | ||||||
| 
 | 
 | ||||||
| 
 |     strategy: | ||||||
|  |       matrix: | ||||||
|  |         flavor: [Net, NoNet] | ||||||
|  |         target: [apk, apk_split] | ||||||
| 
 | 
 | ||||||
|     steps: |     steps: | ||||||
|      |      | ||||||
| @@ -52,7 +54,7 @@ jobs: | |||||||
|       with: |       with: | ||||||
|         minimum-size: 8GB |         minimum-size: 8GB | ||||||
| 
 | 
 | ||||||
|     - name: Add msbuild to PATH |     - name: Add msbuild/dotnet to PATH | ||||||
|       uses: microsoft/setup-msbuild@v2 |       uses: microsoft/setup-msbuild@v2 | ||||||
|       # If we want to also have nmake, use this instead |       # If we want to also have nmake, use this instead | ||||||
|       #uses: ilammy/msvc-dev-cmd@v1 |       #uses: ilammy/msvc-dev-cmd@v1 | ||||||
| @@ -76,38 +78,69 @@ jobs: | |||||||
|       run: | |       run: | | ||||||
|         make java |         make java | ||||||
| 
 | 
 | ||||||
|     - name: Install NuGet dependencies (net) |     - name: List apks | ||||||
|       run: make nuget Flavor=Net |       run:  find . -type f -name "*.apk" | ||||||
|  |       shell: bash | ||||||
| 
 | 
 | ||||||
|     - name: Use the _net manifest |     - name: Update dotnet workloads | ||||||
|       run: | |       run: | | ||||||
|         make manifestlink Flavor=Net |         dotnet workload update         | ||||||
|  | 
 | ||||||
|  |     - name: List apks | ||||||
|  |       run:  find . -type f -name "*.apk" | ||||||
|  |       shell: bash | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |     - name: Select the manifest | ||||||
|  |       run: | | ||||||
|  |         make manifestlink Flavor=${{ matrix.flavor }} | ||||||
|  |      | ||||||
|  |     - name: List apks | ||||||
|  |       run:  find . -type f -name "*.apk" | ||||||
|  |       shell: bash | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     - name: Install NuGet dependencies | ||||||
|  |       run: make nuget Flavor=${{ matrix.flavor }} | ||||||
|  | 
 | ||||||
|  |     - name: List apks | ||||||
|  |       run:  find . -type f -name "*.apk" | ||||||
|  |       shell: bash | ||||||
| 
 | 
 | ||||||
|     - name: Build APK (net) |     - name: Build APK (net) | ||||||
|       env: |       env: | ||||||
|         KeyStore: "${{ github.workspace }}/kp2a.keystore" |         KeyStore: "${{ github.workspace }}/kp2a.keystore" | ||||||
|         MyAndroidSigningStorePass: ${{ secrets.KEY_STORE_PASSWORD }} |         MyAndroidSigningStorePass: ${{ secrets.KEY_STORE_PASSWORD }} | ||||||
|         MyAndroidSigningKeyPass: ${{ secrets.KEY_PASSWORD }} |         MyAndroidSigningKeyPass: ${{ secrets.KEY_PASSWORD }} | ||||||
|       run: | |         DropboxAppKey: ${{ secrets.DROPBOX_APP_KEY }} | ||||||
|         make Configuration=Release Flavor=Net |         DropboxAppSecret: ${{ secrets.DROPBOX_APP_SECRET }} | ||||||
|  |         DropboxAppFolderAppKey: ${{ secrets.DROPBOX_APP_FOLDER_APP_KEY }} | ||||||
|  |         DropboxAppFolderAppSecret: ${{ secrets.DROPBOX_APP_FOLDER_APP_SECRET }} | ||||||
| 
 | 
 | ||||||
|     - name: List files |       run: | | ||||||
|  |         make ${{ matrix.target }} Configuration=Release Flavor=${{ matrix.flavor }} | ||||||
|  |      | ||||||
|  |     - name: List apks | ||||||
|  |       run:  find . -type f -name "*.apk" | ||||||
|       shell: bash |       shell: bash | ||||||
|       run: | |  | ||||||
|         ls src/keepass2android-app/bin/**/*.* |  | ||||||
|      |      | ||||||
|     - name: Archive production artifacts (net) |     - name: Archive production artifacts | ||||||
|       uses: actions/upload-artifact@v4 |       uses: actions/upload-artifact@v4 | ||||||
|       with: |       with: | ||||||
|         name: signed APK ('net' built on ${{ github.job }}) |         name: keepass2android_${{ matrix.target }}_${{ matrix.flavor }} | ||||||
|  |         # the first line is for "apk" target, the second line is for "apk_split" target | ||||||
|         path: | |         path: | | ||||||
|           src/keepass2android-app/bin/Release/net8.0-android/*-Signed.apk |           src/keepass2android-app/bin/Release/net8.0-android/publish/*.apk | ||||||
|  |           src/keepass2android-app/bin/Release/net8.0-android/*/publish/*.apk | ||||||
|  |      | ||||||
|  |     - name: List apks | ||||||
|  |       run:  find . -type f -name "*.apk" | ||||||
|  |       shell: bash | ||||||
|        |        | ||||||
|     - name: Upload APK to GitHub Release |     - name: Upload APK to GitHub Release | ||||||
|       uses: softprops/action-gh-release@v2 |       uses: softprops/action-gh-release@v2 | ||||||
|       if: github.ref_type == 'tag' |       if: github.ref_type == 'tag' | ||||||
|       with: |       with: | ||||||
|         files: | |         files: | | ||||||
|             src/keepass2android-app/bin/Release/net8.0-android/*-Signed.apk |           src/keepass2android-app/bin/Release/net8.0-android/publish/*.apk | ||||||
|  |           src/keepass2android-app/bin/Release/net8.0-android/*/publish/*.apk | ||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -64,7 +64,7 @@ Thumbs.db | |||||||
| /src/java/android-filechooser/code/projectzip/project.zip | /src/java/android-filechooser/code/projectzip/project.zip | ||||||
| /src/java/android-filechooser/code/unused.txt | /src/java/android-filechooser/code/unused.txt | ||||||
|  |  | ||||||
| /src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs | /src/Kp2aBusinessLogic/Io/DropboxFileStorage.g.cs | ||||||
|  |  | ||||||
| /src/java/workspace/DriveTest | /src/java/workspace/DriveTest | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										89
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								Makefile
									
									
									
									
									
								
							| @@ -4,10 +4,10 @@ | |||||||
| # This Makefile can be used on both unix-like (use make) & windows (with GNU make) | # This Makefile can be used on both unix-like (use make) & windows (with GNU make) | ||||||
| # | # | ||||||
| # append the Configuration variable to 'make' call with value to use in '/p:Configuration=' | # append the Configuration variable to 'make' call with value to use in '/p:Configuration=' | ||||||
| # of msbuild command. | # of dotnetbuild command. | ||||||
| # | # | ||||||
| # append the Flavor variable to 'make' call with value to use in '/p:Flavor=' | # append the Flavor variable to 'make' call with value to use in '/p:Flavor=' | ||||||
| # of msbuild command. | # of dotnetbuild command. | ||||||
| # | # | ||||||
| # Example: | # Example: | ||||||
| #    make Configuration=Release Flavor=NoNet | #    make Configuration=Release Flavor=NoNet | ||||||
| @@ -18,7 +18,7 @@ | |||||||
| #  - native: build the native libs | #  - native: build the native libs | ||||||
| #  - java: build the java libs | #  - java: build the java libs | ||||||
| #  - nuget: restore NuGet packages | #  - nuget: restore NuGet packages | ||||||
| #  - msbuild: build the project | #  - dotnetbuild: build the project | ||||||
| #  - apk: same as all | #  - apk: same as all | ||||||
| #  - manifestlink: creates a symlink (to be used in building) to the AndroidManifest corresponding to the selected Flavor | #  - manifestlink: creates a symlink (to be used in building) to the AndroidManifest corresponding to the selected Flavor | ||||||
| # | # | ||||||
| @@ -27,7 +27,7 @@ | |||||||
| #  - clean_native: clean native lib | #  - clean_native: clean native lib | ||||||
| #  - clean_java: call clean target of java libs | #  - clean_java: call clean target of java libs | ||||||
| #  - clean_nuget: cleanup the 'nuget restore' | #  - clean_nuget: cleanup the 'nuget restore' | ||||||
| #  - clean_msbuild: call clean target of msbuild | #  - clean_dotnet: call clean target of dotnetbuild | ||||||
| # | # | ||||||
| # | # | ||||||
| # | # | ||||||
| @@ -60,45 +60,23 @@ $(info MAKESHELL: $(MAKESHELL)) | |||||||
| $(info SHELL: $(SHELL)) | $(info SHELL: $(SHELL)) | ||||||
| $(info ) | $(info ) | ||||||
|  |  | ||||||
| # On linux use xabuild, on Windows use MSBuild.exe, otherwise (macos?) use msbuild. |  | ||||||
| ifeq ($(detected_OS),Linux) | ifeq ($(detected_OS),Linux) | ||||||
|   MSBUILD_binary := xabuild |   DOTNET_binary := dotnet | ||||||
|   MSBUILD := $(shell $(WHICH) $(MSBUILD_binary)) |   DOTNET := $(shell $(WHICH) $(DOTNET_binary)) | ||||||
| else ifeq ($(detected_OS),Windows) | else ifeq ($(detected_OS),Windows) | ||||||
|   MSBUILD_binary := MSBuild.exe |   DOTNET_binary := dotnet | ||||||
|   MSBUILD := $(shell $(WHICH) $(MSBUILD_binary) 2> nul) |   DOTNET := $(shell $(WHICH) $(DOTNET_binary) 2> nul) | ||||||
|   ifeq ($(MSBUILD),) |  | ||||||
|     # Additional heuristic to find MSBUILD_BINARY on Windows |  | ||||||
|     VSWHERE := "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" |  | ||||||
|     VSWHERE_CHECK := $(shell @echo off & $(VSWHERE) 2> nul || echo VSWHERE_NOT_FOUND) |  | ||||||
|     ifneq ($(VSWHERE_CHECK),VSWHERE_NOT_FOUND) |  | ||||||
|       MSBUILD := $(shell @echo off & $(VSWHERE) -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe) |  | ||||||
|       VS_INSTALL_PATH := $(shell @echo off & $(VSWHERE) -property installationPath) |  | ||||||
|     endif |  | ||||||
|   endif |  | ||||||
| else | else | ||||||
|   MSBUILD_binary := msbuild |   DOTNET_binary := dotnet | ||||||
|   MSBUILD := $(shell $(WHICH) $(MSBUILD_binary)) |   DOTNET := $(shell $(WHICH) $(DOTNET_binary)) | ||||||
| endif | endif | ||||||
|  |  | ||||||
| ifeq ($(MSBUILD),) | ifeq ($(DOTNET),) | ||||||
|   $(info ) |   $(info ) | ||||||
|   $(info '$(MSBUILD_binary)' binary could not be found. Check it is in your PATH.) |   $(info '$(DOTNET_binary)' binary could not be found. Check it is in your PATH.) | ||||||
|   ifeq ($(detected_OS),Windows) |  | ||||||
|   ifneq ($(VSWHERE_CHECK),VSWHERE_NOT_FOUND) |  | ||||||
|     $(info ) |  | ||||||
|     $(info You may retry after running in the command prompt:) |  | ||||||
|     $(info ) |  | ||||||
|     $(info "$(VS_INSTALL_PATH)\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64) |  | ||||||
|     $(info ) |  | ||||||
|     $(info If this doesn't work, install/find the location of vcvarsall.bat) |  | ||||||
|     $(info or install and add msbuild.exe to your PATH) |  | ||||||
|     $(info ) |  | ||||||
|   endif |  | ||||||
|   endif |  | ||||||
|   $(error ) |   $(error ) | ||||||
| endif | endif | ||||||
| $(info MSBUILD: $(MSBUILD)) | $(info DOTNET: $(DOTNET)) | ||||||
| $(info ) | $(info ) | ||||||
|  |  | ||||||
| ifeq ($(ANDROID_SDK_ROOT),) | ifeq ($(ANDROID_SDK_ROOT),) | ||||||
| @@ -117,7 +95,7 @@ endif | |||||||
| $(info ANDROID_NDK_ROOT: $(ANDROID_NDK_ROOT)) | $(info ANDROID_NDK_ROOT: $(ANDROID_NDK_ROOT)) | ||||||
|  |  | ||||||
| ifneq ($(Configuration),) | ifneq ($(Configuration),) | ||||||
|   MSBUILD_PARAM = -p:Configuration="$(Configuration)" |   DOTNET_PARAM = -p:Configuration="$(Configuration)" | ||||||
| else | else | ||||||
|   $(warning Configuration environment variable not set.) |   $(warning Configuration environment variable not set.) | ||||||
| endif | endif | ||||||
| @@ -127,7 +105,7 @@ CREATE_MANIFEST_LINK := | |||||||
|  |  | ||||||
| MANIFEST_FILE :=  | MANIFEST_FILE :=  | ||||||
| ifneq ($(Flavor),) | ifneq ($(Flavor),) | ||||||
|   MSBUILD_PARAM += -p:Flavor="$(Flavor)" |   DOTNET_PARAM += -p:Flavor="$(Flavor)" | ||||||
|   ifneq ($(Flavor),) |   ifneq ($(Flavor),) | ||||||
| 		ifeq ($(Flavor),Debug) | 		ifeq ($(Flavor),Debug) | ||||||
| 			MANIFEST_FILE := AndroidManifest_debug.xml | 			MANIFEST_FILE := AndroidManifest_debug.xml | ||||||
| @@ -152,7 +130,7 @@ else | |||||||
| endif | endif | ||||||
|  |  | ||||||
| ifneq ($(KeyStore),) | ifneq ($(KeyStore),) | ||||||
|   MSBUILD_PARAM += -p:AndroidKeyStore=True -p:AndroidSigningKeyStore="$(KeyStore)" -p:AndroidSigningStorePass=env:MyAndroidSigningStorePass -p:AndroidSigningKeyPass=env:MyAndroidSigningKeyPass -p:AndroidSigningKeyAlias="kp2a" |   DOTNET_PARAM += -p:AndroidKeyStore=True -p:AndroidSigningKeyStore="$(KeyStore)" -p:AndroidSigningStorePass=env:MyAndroidSigningStorePass -p:AndroidSigningKeyPass=env:MyAndroidSigningKeyPass -p:AndroidSigningKeyAlias="kp2a" | ||||||
| endif | endif | ||||||
|  |  | ||||||
| ifeq ($(detected_OS),Windows) | ifeq ($(detected_OS),Windows) | ||||||
| @@ -176,7 +154,7 @@ endif | |||||||
| # Recursive wildcard: https://stackoverflow.com/a/18258352 | # Recursive wildcard: https://stackoverflow.com/a/18258352 | ||||||
| rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) | rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) | ||||||
|  |  | ||||||
| $(info MSBUILD_PARAM: $(MSBUILD_PARAM)) | $(info DOTNET_PARAM: $(DOTNET_PARAM)) | ||||||
| $(info nuget path: $(shell $(WHICH) nuget)) | $(info nuget path: $(shell $(WHICH) nuget)) | ||||||
| $(info ) | $(info ) | ||||||
|  |  | ||||||
| @@ -254,7 +232,7 @@ OUTPUT_PluginQR = src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/Keepa | |||||||
| .PHONY: native $(NATIVE_COMPONENTS) clean_native $(NATIVE_CLEAN_TARGETS) \ | .PHONY: native $(NATIVE_COMPONENTS) clean_native $(NATIVE_CLEAN_TARGETS) \ | ||||||
| 	java $(JAVA_COMPONENTS) clean_java $(JAVA_CLEAN_TARGETS) \ | 	java $(JAVA_COMPONENTS) clean_java $(JAVA_CLEAN_TARGETS) \ | ||||||
| 	nuget clean_nuget \ | 	nuget clean_nuget \ | ||||||
| 	msbuild clean_msbuild \ | 	dotnetbuild clean_dotnet \ | ||||||
| 	apk all clean | 	apk all clean | ||||||
|  |  | ||||||
| all: apk | all: apk | ||||||
| @@ -303,7 +281,7 @@ ifeq ($(shell $(WHICH) nuget),) | |||||||
| endif | endif | ||||||
| 	$(RMFILE) stamp.nuget_* | 	$(RMFILE) stamp.nuget_* | ||||||
| 	nuget restore src/KeePass.sln | 	nuget restore src/KeePass.sln | ||||||
| 	$(MSBUILD) src/KeePass.sln -t:restore $(MSBUILD_PARAM) -p:RestorePackagesConfig=true | 	$(DOTNET) restore src/KeePass.sln $(DOTNET_PARAM) -p:RestorePackagesConfig=true | ||||||
| 	@echo "" > stamp.nuget_$(Flavor) | 	@echo "" > stamp.nuget_$(Flavor) | ||||||
|  |  | ||||||
| manifestlink: | manifestlink: | ||||||
| @@ -312,20 +290,21 @@ manifestlink: | |||||||
| 	$(CREATE_MANIFEST_LINK)	 | 	$(CREATE_MANIFEST_LINK)	 | ||||||
|  |  | ||||||
| ##### | ##### | ||||||
| src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs: |  | ||||||
| ifeq ($(detected_OS),Windows) |  | ||||||
| 	$(CP) src\Kp2aBusinessLogic\Io\DropboxFileStorageKeysDummy.cs src\Kp2aBusinessLogic\Io\DropboxFileStorageKeys.cs |  | ||||||
| else |  | ||||||
| 	$(CP) src/Kp2aBusinessLogic/Io/DropboxFileStorageKeysDummy.cs $@ |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| msbuild: manifestlink native java nuget src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs | dotnetbuild: manifestlink native java nuget  | ||||||
| 	$(MSBUILD) src/KeePass.sln -target:keepass2android-app -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -p:BuildProjectReferences=true $(MSBUILD_PARAM) -p:Platform="Any CPU" -m | 	$(DOTNET) build src/KeePass.sln -target:keepass2android-app -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -p:BuildProjectReferences=true $(DOTNET_PARAM) -p:Platform="Any CPU" -m | ||||||
|  |  | ||||||
| apk: msbuild  | apk: manifestlink native java nuget   | ||||||
| 	$(MSBUILD) src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(MSBUILD_PARAM) -p:Platform=AnyCPU -m  | 	$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m  | ||||||
|  |  | ||||||
| build_all: msbuild | apk_split: manifestlink native java nuget  | ||||||
|  | 	$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-arm | ||||||
|  | 	$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-arm64 | ||||||
|  | 	$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-x86 | ||||||
|  | 	$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-x64 | ||||||
|  | 	src/build-scripts/rename-output-apks.sh src/keepass2android-app/bin/Release/net8.0-android/ | ||||||
|  |  | ||||||
|  | build_all: dotnetbuild | ||||||
|  |  | ||||||
| ##### Cleanup targets | ##### Cleanup targets | ||||||
|  |  | ||||||
| @@ -369,10 +348,10 @@ else | |||||||
| endif | endif | ||||||
| 	$(RMFILE) stamp.nuget_* | 	$(RMFILE) stamp.nuget_* | ||||||
|  |  | ||||||
| clean_msbuild: | clean_dotnet: | ||||||
| 	$(MSBUILD) src/KeePass.sln -target:clean $(MSBUILD_PARAM) | 	$(DOTNET) clean src/KeePass.sln $(DOTNET_PARAM) | ||||||
|  |  | ||||||
| clean: clean_native clean_java clean_nuget clean_msbuild | clean: clean_native clean_java clean_nuget clean_dotnet | ||||||
|  |  | ||||||
| distclean: clean | distclean: clean | ||||||
| ifneq ("$(wildcard ./allow_git_clean)","") | ifneq ("$(wildcard ./allow_git_clean)","") | ||||||
|   | |||||||
| @@ -11,10 +11,10 @@ Regular stable releases of Keepass2Android are available on [Google Play](https: | |||||||
| Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet). | Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet). | ||||||
|  |  | ||||||
| # How can I contribute? | # How can I contribute? | ||||||
| * Help to translate Keepass2Android into your language or improve translations at [our Crowdin page](http://crowdin.net/project/keepass2android) | * Help to translate Keepass2Android into your language or improve translations at [our Crowdin page](https://crowdin.net/project/keepass2android) | ||||||
| * Add features by [creating a plugin](How-to-create-a-plug-in_.md) or creating a pull request. You might want to contact me before you start working so I can coordinate efforts. | * Add features by [creating a plugin](How-to-create-a-plug-in_.md) or creating a pull request. You might want to contact me before you start working so I can coordinate efforts. | ||||||
| * [Become a GitHub sponsor to boost 🚀 development](https://github.com/sponsors/PhilippC) | * [Become a GitHub sponsor to boost 🚀 development](https://github.com/sponsors/PhilippC) | ||||||
| * [Make a donation](http://philipp.crocoll.net/donate.php) | * [Make a donation](https://philipp.crocoll.net/donate.php) | ||||||
|  |  | ||||||
| # How do I learn more? | # How do I learn more? | ||||||
| Please see the [wiki](https://github.com/PhilippC/keepass2android/wiki/Documentation) for further information. | Please see the [wiki](https://github.com/PhilippC/keepass2android/wiki/Documentation) for further information. | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
|   KeePass Password Safe - The Open-Source Password Manager |   KeePass Password Safe - The Open-Source Password Manager | ||||||
|   Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> |   Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de> | ||||||
|  |  | ||||||
|   This program is free software; you can redistribute it and/or modify |   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 |   it under the terms of the GNU General Public License as published by | ||||||
| @@ -40,8 +40,8 @@ namespace KeePassLib.Cryptography | |||||||
|         Null = 0, |         Null = 0, | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
| 		/// A variant of the ARCFour algorithm (RC4 incompatible). |         /// A variant of the ArcFour algorithm (RC4 incompatible). | ||||||
| 		/// </summary> |         /// Insecure; for backward compatibility only. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         ArcFourVariant = 1, |         ArcFourVariant = 1, | ||||||
|  |  | ||||||
| @@ -66,56 +66,60 @@ namespace KeePassLib.Cryptography | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     public sealed class CryptoRandomStream : IDisposable |     public sealed class CryptoRandomStream : IDisposable | ||||||
|     { |     { | ||||||
| 		private readonly CrsAlgorithm m_crsAlgorithm; |         private readonly CrsAlgorithm m_alg; | ||||||
|  |         private bool m_bDisposed = false; | ||||||
|  |  | ||||||
| 		private byte[] m_pbState = null; |         private readonly byte[] m_pbKey = null; | ||||||
|  |         private readonly byte[] m_pbIV = null; | ||||||
|  |  | ||||||
|  |         private readonly ChaCha20Cipher m_chacha20 = null; | ||||||
|  |         private readonly Salsa20Cipher m_salsa20 = null; | ||||||
|  |  | ||||||
|  |         private readonly byte[] m_pbState = null; | ||||||
|         private byte m_i = 0; |         private byte m_i = 0; | ||||||
|         private byte m_j = 0; |         private byte m_j = 0; | ||||||
|  |  | ||||||
| 		private Salsa20Cipher m_salsa20 = null; |  | ||||||
| 		private ChaCha20Cipher m_chacha20 = null; |  | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Construct a new cryptographically secure random stream object. |         /// Construct a new cryptographically secure random stream object. | ||||||
|         /// </summary> |         /// </summary> | ||||||
| 		/// <param name="genAlgorithm">Algorithm to use.</param> |         /// <param name="a">Algorithm to use.</param> | ||||||
| 		/// <param name="pbKey">Initialization key. Must not be <c>null</c> and |         /// <param name="pbKey">Initialization key. Must not be <c>null</c> | ||||||
| 		/// must contain at least 1 byte.</param> |         /// and must contain at least 1 byte.</param> | ||||||
|         public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey) |         public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey) | ||||||
|         { |         { | ||||||
|             if (pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); } |             if (pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); } | ||||||
| 		/// <exception cref="System.ArgumentNullException">Thrown if the |  | ||||||
|             int cbKey = pbKey.Length; |             int cbKey = pbKey.Length; | ||||||
|             if (cbKey <= 0) |             if (cbKey <= 0) | ||||||
|             { |             { | ||||||
|                 Debug.Assert(false); // Need at least one byte |                 Debug.Assert(false); // Need at least one byte | ||||||
|                 throw new ArgumentOutOfRangeException("pbKey"); |                 throw new ArgumentOutOfRangeException("pbKey"); | ||||||
|             } |             } | ||||||
| 		/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception> |  | ||||||
| 			m_crsAlgorithm = a; |             m_alg = a; | ||||||
| 		/// <exception cref="System.ArgumentException">Thrown if the |  | ||||||
|             if (a == CrsAlgorithm.ChaCha20) |             if (a == CrsAlgorithm.ChaCha20) | ||||||
|             { |             { | ||||||
| 				byte[] pbKey32 = new byte[32]; |                 m_pbKey = new byte[32]; | ||||||
| 				byte[] pbIV12 = new byte[12]; |                 m_pbIV = new byte[12]; | ||||||
| 		/// <paramref name="pbKey" /> parameter contains no bytes or the |  | ||||||
|                 using (SHA512Managed h = new SHA512Managed()) |                 using (SHA512Managed h = new SHA512Managed()) | ||||||
|                 { |                 { | ||||||
|                     byte[] pbHash = h.ComputeHash(pbKey); |                     byte[] pbHash = h.ComputeHash(pbKey); | ||||||
| 					Array.Copy(pbHash, pbKey32, 32); |                     Array.Copy(pbHash, m_pbKey, 32); | ||||||
| 					Array.Copy(pbHash, 32, pbIV12, 0, 12); |                     Array.Copy(pbHash, 32, m_pbIV, 0, 12); | ||||||
|                     MemUtil.ZeroByteArray(pbHash); |                     MemUtil.ZeroByteArray(pbHash); | ||||||
|                 } |                 } | ||||||
| 		/// algorithm is unknown.</exception> |  | ||||||
| 				m_chacha20 = new ChaCha20Cipher(pbKey32, pbIV12, true); |                 m_chacha20 = new ChaCha20Cipher(m_pbKey, m_pbIV, true); | ||||||
|             } |             } | ||||||
|             else if (a == CrsAlgorithm.Salsa20) |             else if (a == CrsAlgorithm.Salsa20) | ||||||
|             { |             { | ||||||
| 				byte[] pbKey32 = CryptoUtil.HashSha256(pbKey); |                 m_pbKey = CryptoUtil.HashSha256(pbKey); | ||||||
| 				byte[] pbIV8 = new byte[8] { 0xE8, 0x30, 0x09, 0x4B, |                 m_pbIV = new byte[8] { 0xE8, 0x30, 0x09, 0x4B, | ||||||
|                     0x97, 0x20, 0x5D, 0x2A }; // Unique constant |                     0x97, 0x20, 0x5D, 0x2A }; // Unique constant | ||||||
|  |  | ||||||
| 				m_salsa20 = new Salsa20Cipher(pbKey32, pbIV8); |                 m_salsa20 = new Salsa20Cipher(m_pbKey, m_pbIV); | ||||||
|             } |             } | ||||||
|             else if (a == CrsAlgorithm.ArcFourVariant) |             else if (a == CrsAlgorithm.ArcFourVariant) | ||||||
|             { |             { | ||||||
| @@ -159,17 +163,22 @@ namespace KeePassLib.Cryptography | |||||||
|         { |         { | ||||||
|             if (disposing) |             if (disposing) | ||||||
|             { |             { | ||||||
| 				if(m_crsAlgorithm == CrsAlgorithm.ChaCha20) |                 if (m_alg == CrsAlgorithm.ChaCha20) | ||||||
|                     m_chacha20.Dispose(); |                     m_chacha20.Dispose(); | ||||||
| 				else if(m_crsAlgorithm == CrsAlgorithm.Salsa20) |                 else if (m_alg == CrsAlgorithm.Salsa20) | ||||||
|                     m_salsa20.Dispose(); |                     m_salsa20.Dispose(); | ||||||
| 				else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant) |                 else if (m_alg == CrsAlgorithm.ArcFourVariant) | ||||||
|                 { |                 { | ||||||
|                     MemUtil.ZeroByteArray(m_pbState); |                     MemUtil.ZeroByteArray(m_pbState); | ||||||
|                     m_i = 0; |                     m_i = 0; | ||||||
|                     m_j = 0; |                     m_j = 0; | ||||||
|                 } |                 } | ||||||
|                 else { Debug.Assert(false); } |                 else { Debug.Assert(false); } | ||||||
|  |  | ||||||
|  |                 if (m_pbKey != null) MemUtil.ZeroByteArray(m_pbKey); | ||||||
|  |                 if (m_pbIV != null) MemUtil.ZeroByteArray(m_pbIV); | ||||||
|  |  | ||||||
|  |                 m_bDisposed = true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -180,19 +189,20 @@ namespace KeePassLib.Cryptography | |||||||
|         /// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns> |         /// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns> | ||||||
|         public byte[] GetRandomBytes(uint uRequestedCount) |         public byte[] GetRandomBytes(uint uRequestedCount) | ||||||
|         { |         { | ||||||
| 			if(uRequestedCount == 0) return MemUtil.EmptyByteArray; |             if (m_bDisposed) throw new ObjectDisposedException(null); | ||||||
|  |  | ||||||
|  |             if (uRequestedCount == 0) return MemUtil.EmptyByteArray; | ||||||
|             if (uRequestedCount > (uint)int.MaxValue) |             if (uRequestedCount > (uint)int.MaxValue) | ||||||
|                 throw new ArgumentOutOfRangeException("uRequestedCount"); |                 throw new ArgumentOutOfRangeException("uRequestedCount"); | ||||||
|             int cb = (int)uRequestedCount; |             int cb = (int)uRequestedCount; | ||||||
|  |  | ||||||
|             byte[] pbRet = new byte[cb]; |             byte[] pbRet = new byte[cb]; | ||||||
|  |  | ||||||
| 			if(m_crsAlgorithm == CrsAlgorithm.ChaCha20) |             if (m_alg == CrsAlgorithm.ChaCha20) | ||||||
|                 m_chacha20.Encrypt(pbRet, 0, cb); |                 m_chacha20.Encrypt(pbRet, 0, cb); | ||||||
| 			else if(m_crsAlgorithm == CrsAlgorithm.Salsa20) |             else if (m_alg == CrsAlgorithm.Salsa20) | ||||||
|                 m_salsa20.Encrypt(pbRet, 0, cb); |                 m_salsa20.Encrypt(pbRet, 0, cb); | ||||||
| 			else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant) |             else if (m_alg == CrsAlgorithm.ArcFourVariant) | ||||||
|             { |             { | ||||||
|                 unchecked |                 unchecked | ||||||
|                 { |                 { | ||||||
| @@ -221,6 +231,25 @@ namespace KeePassLib.Cryptography | |||||||
|             return MemUtil.BytesToUInt64(pb); |             return MemUtil.BytesToUInt64(pb); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         internal ulong GetRandomUInt64(ulong uMaxExcl) | ||||||
|  |         { | ||||||
|  |             if (uMaxExcl == 0) { Debug.Assert(false); throw new ArgumentOutOfRangeException("uMaxExcl"); } | ||||||
|  |  | ||||||
|  |             ulong uGen, uRem; | ||||||
|  |             do | ||||||
|  |             { | ||||||
|  |                 uGen = GetRandomUInt64(); | ||||||
|  |                 uRem = uGen % uMaxExcl; | ||||||
|  |             } | ||||||
|  |             while ((uGen - uRem) > (ulong.MaxValue - (uMaxExcl - 1UL))); | ||||||
|  |             // This ensures that the last number of the block (i.e. | ||||||
|  |             // (uGen - uRem) + (uMaxExcl - 1)) is generatable; | ||||||
|  |             // for signed longs, overflow to negative number: | ||||||
|  |             // while((uGen - uRem) + (uMaxExcl - 1) < 0); | ||||||
|  |  | ||||||
|  |             return uRem; | ||||||
|  |         } | ||||||
|  |  | ||||||
| #if CRSBENCHMARK | #if CRSBENCHMARK | ||||||
| 		public static string Benchmark() | 		public static string Benchmark() | ||||||
| 		{ | 		{ | ||||||
| @@ -237,21 +266,20 @@ namespace KeePassLib.Cryptography | |||||||
| 			return str; | 			return str; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private static int BenchTime(CrsAlgorithm cra, int nRounds, int nDataSize) | 		private static int BenchTime(CrsAlgorithm a, int nRounds, int cbData) | ||||||
| 		{ | 		{ | ||||||
| 			byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 }; | 			byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 }; | ||||||
|  |  | ||||||
| 			int nStart = Environment.TickCount; | 			int tStart = Environment.TickCount; | ||||||
| 			for(int i = 0; i < nRounds; ++i) | 			for(int i = 0; i < nRounds; ++i) | ||||||
| 			{ | 			{ | ||||||
| 				using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey)) | 				using(CryptoRandomStream crs = new CryptoRandomStream(a, pbKey)) | ||||||
| 				{ | 				{ | ||||||
| 					c.GetRandomBytes((uint)nDataSize); | 					crs.GetRandomBytes((uint)cbData); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			int nEnd = Environment.TickCount; |  | ||||||
|  |  | ||||||
| 			return (nEnd - nStart); | 			return (Environment.TickCount - tStart); | ||||||
| 		} | 		} | ||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,65 +0,0 @@ | |||||||
| /* |  | ||||||
|   KeePass Password Safe - The Open-Source Password Manager |  | ||||||
|   Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> |  | ||||||
|  |  | ||||||
|   This program is free software; you can redistribute it and/or modify |  | ||||||
|   it under the terms of the GNU General Public License as published by |  | ||||||
|   the Free Software Foundation; either version 2 of the License, or |  | ||||||
|   (at your option) any later version. |  | ||||||
|  |  | ||||||
|   This program is distributed in the hope that it will be useful, |  | ||||||
|   but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|   GNU General Public License for more details. |  | ||||||
|  |  | ||||||
|   You should have received a copy of the GNU General Public License |  | ||||||
|   along with this program; if not, write to the Free Software |  | ||||||
|   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Text; |  | ||||||
| using System.Diagnostics; |  | ||||||
|  |  | ||||||
| using KeePassLib.Security; |  | ||||||
| using KeePassLib.Utility; |  | ||||||
|  |  | ||||||
| namespace KeePassLib.Cryptography.PasswordGenerator |  | ||||||
| { |  | ||||||
| 	internal static class CharSetBasedGenerator |  | ||||||
| 	{ |  | ||||||
| 		internal static PwgError Generate(out ProtectedString psOut, |  | ||||||
| 			PwProfile pwProfile, CryptoRandomStream crsRandomSource) |  | ||||||
| 		{ |  | ||||||
| 			psOut = ProtectedString.Empty; |  | ||||||
| 			if(pwProfile.Length == 0) return PwgError.Success; |  | ||||||
|  |  | ||||||
| 			PwCharSet pcs = new PwCharSet(pwProfile.CharSet.ToString()); |  | ||||||
| 			char[] vGenerated = new char[pwProfile.Length]; |  | ||||||
|  |  | ||||||
| 			PwGenerator.PrepareCharSet(pcs, pwProfile); |  | ||||||
|  |  | ||||||
| 			for(int nIndex = 0; nIndex < (int)pwProfile.Length; ++nIndex) |  | ||||||
| 			{ |  | ||||||
| 				char ch = PwGenerator.GenerateCharacter(pwProfile, pcs, |  | ||||||
| 					crsRandomSource); |  | ||||||
|  |  | ||||||
| 				if(ch == char.MinValue) |  | ||||||
| 				{ |  | ||||||
| 					MemUtil.ZeroArray<char>(vGenerated); |  | ||||||
| 					return PwgError.TooFewCharacters; |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				vGenerated[nIndex] = ch; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vGenerated); |  | ||||||
| 			psOut = new ProtectedString(true, pbUtf8); |  | ||||||
| 			MemUtil.ZeroByteArray(pbUtf8); |  | ||||||
| 			MemUtil.ZeroArray<char>(vGenerated); |  | ||||||
|  |  | ||||||
| 			return PwgError.Success; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,173 +0,0 @@ | |||||||
| /* |  | ||||||
|   KeePass Password Safe - The Open-Source Password Manager |  | ||||||
|   Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> |  | ||||||
|  |  | ||||||
|   This program is free software; you can redistribute it and/or modify |  | ||||||
|   it under the terms of the GNU General Public License as published by |  | ||||||
|   the Free Software Foundation; either version 2 of the License, or |  | ||||||
|   (at your option) any later version. |  | ||||||
|  |  | ||||||
|   This program is distributed in the hope that it will be useful, |  | ||||||
|   but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|   GNU General Public License for more details. |  | ||||||
|  |  | ||||||
|   You should have received a copy of the GNU General Public License |  | ||||||
|   along with this program; if not, write to the Free Software |  | ||||||
|   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Text; |  | ||||||
| using System.Diagnostics; |  | ||||||
|  |  | ||||||
| using KeePassLib.Security; |  | ||||||
| using KeePassLib.Utility; |  | ||||||
|  |  | ||||||
| namespace KeePassLib.Cryptography.PasswordGenerator |  | ||||||
| { |  | ||||||
| 	internal static class PatternBasedGenerator |  | ||||||
| 	{ |  | ||||||
| 		internal static PwgError Generate(out ProtectedString psOut, |  | ||||||
| 			PwProfile pwProfile, CryptoRandomStream crsRandomSource) |  | ||||||
| 		{ |  | ||||||
| 			psOut = ProtectedString.Empty; |  | ||||||
| 			LinkedList<char> vGenerated = new LinkedList<char>(); |  | ||||||
| 			PwCharSet pcsCurrent = new PwCharSet(); |  | ||||||
| 			PwCharSet pcsCustom = new PwCharSet(); |  | ||||||
| 			PwCharSet pcsUsed = new PwCharSet(); |  | ||||||
| 			bool bInCharSetDef = false; |  | ||||||
|  |  | ||||||
| 			string strPattern = ExpandPattern(pwProfile.Pattern); |  | ||||||
| 			if(strPattern.Length == 0) return PwgError.Success; |  | ||||||
|  |  | ||||||
| 			CharStream csStream = new CharStream(strPattern); |  | ||||||
| 			char ch = csStream.ReadChar(); |  | ||||||
|  |  | ||||||
| 			while(ch != char.MinValue) |  | ||||||
| 			{ |  | ||||||
| 				pcsCurrent.Clear(); |  | ||||||
|  |  | ||||||
| 				bool bGenerateChar = false; |  | ||||||
|  |  | ||||||
| 				if(ch == '\\') |  | ||||||
| 				{ |  | ||||||
| 					ch = csStream.ReadChar(); |  | ||||||
| 					if(ch == char.MinValue) // Backslash at the end |  | ||||||
| 					{ |  | ||||||
| 						vGenerated.AddLast('\\'); |  | ||||||
| 						break; |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
| 					if(bInCharSetDef) pcsCustom.Add(ch); |  | ||||||
| 					else |  | ||||||
| 					{ |  | ||||||
| 						vGenerated.AddLast(ch); |  | ||||||
| 						pcsUsed.Add(ch); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				else if(ch == '[') |  | ||||||
| 				{ |  | ||||||
| 					pcsCustom.Clear(); |  | ||||||
| 					bInCharSetDef = true; |  | ||||||
| 				} |  | ||||||
| 				else if(ch == ']') |  | ||||||
| 				{ |  | ||||||
| 					pcsCurrent.Add(pcsCustom.ToString()); |  | ||||||
|  |  | ||||||
| 					bInCharSetDef = false; |  | ||||||
| 					bGenerateChar = true; |  | ||||||
| 				} |  | ||||||
| 				else if(bInCharSetDef) |  | ||||||
| 				{ |  | ||||||
| 					if(pcsCustom.AddCharSet(ch) == false) |  | ||||||
| 						pcsCustom.Add(ch); |  | ||||||
| 				} |  | ||||||
| 				else if(pcsCurrent.AddCharSet(ch) == false) |  | ||||||
| 				{ |  | ||||||
| 					vGenerated.AddLast(ch); |  | ||||||
| 					pcsUsed.Add(ch); |  | ||||||
| 				} |  | ||||||
| 				else bGenerateChar = true; |  | ||||||
|  |  | ||||||
| 				if(bGenerateChar) |  | ||||||
| 				{ |  | ||||||
| 					PwGenerator.PrepareCharSet(pcsCurrent, pwProfile); |  | ||||||
|  |  | ||||||
| 					if(pwProfile.NoRepeatingCharacters) |  | ||||||
| 						pcsCurrent.Remove(pcsUsed.ToString()); |  | ||||||
|  |  | ||||||
| 					char chGen = PwGenerator.GenerateCharacter(pwProfile, |  | ||||||
| 						pcsCurrent, crsRandomSource); |  | ||||||
|  |  | ||||||
| 					if(chGen == char.MinValue) return PwgError.TooFewCharacters; |  | ||||||
|  |  | ||||||
| 					vGenerated.AddLast(chGen); |  | ||||||
| 					pcsUsed.Add(chGen); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				ch = csStream.ReadChar(); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if(vGenerated.Count == 0) return PwgError.Success; |  | ||||||
|  |  | ||||||
| 			char[] vArray = new char[vGenerated.Count]; |  | ||||||
| 			vGenerated.CopyTo(vArray, 0); |  | ||||||
|  |  | ||||||
| 			if(pwProfile.PatternPermutePassword) |  | ||||||
| 				PwGenerator.ShufflePassword(vArray, crsRandomSource); |  | ||||||
|  |  | ||||||
| 			byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vArray); |  | ||||||
| 			psOut = new ProtectedString(true, pbUtf8); |  | ||||||
| 			MemUtil.ZeroByteArray(pbUtf8); |  | ||||||
| 			MemUtil.ZeroArray<char>(vArray); |  | ||||||
| 			vGenerated.Clear(); |  | ||||||
|  |  | ||||||
| 			return PwgError.Success; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private static string ExpandPattern(string strPattern) |  | ||||||
| 		{ |  | ||||||
| 			Debug.Assert(strPattern != null); if(strPattern == null) return string.Empty; |  | ||||||
| 			string str = strPattern; |  | ||||||
|  |  | ||||||
| 			while(true) |  | ||||||
| 			{ |  | ||||||
| 				int nOpen = FindFirstUnescapedChar(str, '{'); |  | ||||||
| 				int nClose = FindFirstUnescapedChar(str, '}'); |  | ||||||
|  |  | ||||||
| 				if((nOpen >= 0) && (nOpen < nClose)) |  | ||||||
| 				{ |  | ||||||
| 					string strCount = str.Substring(nOpen + 1, nClose - nOpen - 1); |  | ||||||
| 					str = str.Remove(nOpen, nClose - nOpen + 1); |  | ||||||
|  |  | ||||||
| 					uint uRepeat; |  | ||||||
| 					if(StrUtil.TryParseUInt(strCount, out uRepeat) && (nOpen >= 1)) |  | ||||||
| 					{ |  | ||||||
| 						if(uRepeat == 0) |  | ||||||
| 							str = str.Remove(nOpen - 1, 1); |  | ||||||
| 						else |  | ||||||
| 							str = str.Insert(nOpen, new string(str[nOpen - 1], (int)uRepeat - 1)); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				else break; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return str; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private static int FindFirstUnescapedChar(string str, char ch) |  | ||||||
| 		{ |  | ||||||
| 			for(int i = 0; i < str.Length; ++i) |  | ||||||
| 			{ |  | ||||||
| 				char chCur = str[i]; |  | ||||||
|  |  | ||||||
| 				if(chCur == '\\') ++i; // Next is escaped, skip it |  | ||||||
| 				else if(chCur == ch) return i; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return -1; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
|   KeePass Password Safe - The Open-Source Password Manager |   KeePass Password Safe - The Open-Source Password Manager | ||||||
|   Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> |   Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de> | ||||||
|  |  | ||||||
|   This program is free software; you can redistribute it and/or modify |   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 |   it under the terms of the GNU General Public License as published by | ||||||
| @@ -19,122 +19,81 @@ | |||||||
|  |  | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Text; |  | ||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
|  | using KeePassLib.Utility; | ||||||
|  |  | ||||||
| namespace KeePassLib.Cryptography.PasswordGenerator | namespace KeePassLib.Cryptography.PasswordGenerator | ||||||
| { | { | ||||||
| 	public sealed class PwCharSet |     public sealed class PwCharSet : IEquatable<PwCharSet> | ||||||
|     { |     { | ||||||
| 		public const string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |         public static readonly string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||||||
| 		public const string LowerCase = "abcdefghijklmnopqrstuvwxyz"; |         public static readonly string LowerCase = "abcdefghijklmnopqrstuvwxyz"; | ||||||
| 		public const string Digits = "0123456789"; |         public static readonly string Digits = "0123456789"; | ||||||
|  |  | ||||||
| 		public const string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ"; |         public static readonly string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ"; | ||||||
| 		public const string LowerConsonants = "bcdfghjklmnpqrstvwxyz"; |         public static readonly string LowerConsonants = "bcdfghjklmnpqrstvwxyz"; | ||||||
| 		public const string UpperVowels = "AEIOU"; |         public static readonly string UpperVowels = "AEIOU"; | ||||||
| 		public const string LowerVowels = "aeiou"; |         public static readonly string LowerVowels = "aeiou"; | ||||||
|  |  | ||||||
| 		public const string Punctuation = @",.;:"; |         public static readonly string Punctuation = ",.;:"; | ||||||
| 		public const string Brackets = @"[]{}()<>"; |         public static readonly string Brackets = @"[]{}()<>"; | ||||||
|  |  | ||||||
| 		public const string PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; |         public static readonly string Special = "!\"#$%&'*+,./:;=?@\\^`|~"; | ||||||
|  |         public static readonly string PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; | ||||||
|  |  | ||||||
| 		public const string UpperHex = "0123456789ABCDEF"; |         public static readonly string UpperHex = "0123456789ABCDEF"; | ||||||
| 		public const string LowerHex = "0123456789abcdef"; |         public static readonly string LowerHex = "0123456789abcdef"; | ||||||
|  |  | ||||||
| 		public const string Invalid = "\t\r\n"; |         public static readonly string LookAlike = "O0Il1|"; | ||||||
| 		public const string LookAlike = @"O0l1I|"; |  | ||||||
|  |  | ||||||
| 		internal const string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits; |  | ||||||
|  |  | ||||||
| 		private const int CharTabSize = (0x10000 / 8); |  | ||||||
|  |  | ||||||
| 		private List<char> m_vChars = new List<char>(); |  | ||||||
| 		private byte[] m_vTab = new byte[CharTabSize]; |  | ||||||
|  |  | ||||||
| 		private static string m_strHighAnsi = null; |  | ||||||
| 		public static string HighAnsiChars |  | ||||||
| 		{ |  | ||||||
| 			get |  | ||||||
| 			{ |  | ||||||
| 				if(m_strHighAnsi == null) { new PwCharSet(); } // Create string |  | ||||||
| 				Debug.Assert(m_strHighAnsi != null); |  | ||||||
| 				return m_strHighAnsi; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private static string m_strSpecial = null; |  | ||||||
| 		public static string SpecialChars |  | ||||||
| 		{ |  | ||||||
| 			get |  | ||||||
| 			{ |  | ||||||
| 				if(m_strSpecial == null) { new PwCharSet(); } // Create string |  | ||||||
| 				Debug.Assert(m_strSpecial != null); |  | ||||||
| 				return m_strSpecial; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
| 		/// Create a new, empty character set collection object. |         /// Latin-1 Supplement except U+00A0 (NBSP) and U+00AD (SHY). | ||||||
|  |         /// </summary> | ||||||
|  |         public static readonly string Latin1S = | ||||||
|  |             "\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7" + | ||||||
|  |             "\u00A8\u00A9\u00AA\u00AB\u00AC\u00AE\u00AF" + | ||||||
|  |             "\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7" + | ||||||
|  |             "\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF" + | ||||||
|  |             "\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7" + | ||||||
|  |             "\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF" + | ||||||
|  |             "\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7" + | ||||||
|  |             "\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF" + | ||||||
|  |             "\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7" + | ||||||
|  |             "\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF" + | ||||||
|  |             "\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7" + | ||||||
|  |             "\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF"; | ||||||
|  |  | ||||||
|  |         // internal static readonly string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits; | ||||||
|  |  | ||||||
|  |         [Obsolete] | ||||||
|  |         public static string SpecialChars { get { return PwCharSet.Special; } } | ||||||
|  |         [Obsolete] | ||||||
|  |         public static string HighAnsiChars { get { return PwCharSet.Latin1S; } } | ||||||
|  |  | ||||||
|  |         private readonly List<char> m_lChars = new List<char>(); | ||||||
|  |         private readonly byte[] m_vTab = new byte[0x10000 / 8]; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Create a new, empty character set. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public PwCharSet() |         public PwCharSet() | ||||||
|         { |         { | ||||||
| 			Initialize(true); |             Debug.Assert(PwCharSet.Latin1S.Length == (16 * 6 - 2)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public PwCharSet(string strCharSet) |         public PwCharSet(string strCharSet) | ||||||
|         { |         { | ||||||
| 			Initialize(true); |  | ||||||
|             Add(strCharSet); |             Add(strCharSet); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| 		private PwCharSet(bool bFullInitialize) |  | ||||||
| 		{ |  | ||||||
| 			Initialize(bFullInitialize); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private void Initialize(bool bFullInitialize) |  | ||||||
| 		{ |  | ||||||
| 			Clear(); |  | ||||||
|  |  | ||||||
| 			if(!bFullInitialize) return; |  | ||||||
|  |  | ||||||
| 			if(m_strHighAnsi == null) |  | ||||||
| 			{ |  | ||||||
| 				StringBuilder sbHighAnsi = new StringBuilder(); |  | ||||||
| 				// [U+0080, U+009F] are C1 control characters, |  | ||||||
| 				// U+00A0 is non-breaking space |  | ||||||
| 				for(char ch = '\u00A1'; ch <= '\u00AC'; ++ch) |  | ||||||
| 					sbHighAnsi.Append(ch); |  | ||||||
| 				// U+00AD is soft hyphen (format character) |  | ||||||
| 				for(char ch = '\u00AE'; ch < '\u00FF'; ++ch) |  | ||||||
| 					sbHighAnsi.Append(ch); |  | ||||||
| 				sbHighAnsi.Append('\u00FF'); |  | ||||||
|  |  | ||||||
| 				m_strHighAnsi = sbHighAnsi.ToString(); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if(m_strSpecial == null) |  | ||||||
| 			{ |  | ||||||
| 				PwCharSet pcs = new PwCharSet(false); |  | ||||||
| 				pcs.AddRange('!', '/'); |  | ||||||
| 				pcs.AddRange(':', '@'); |  | ||||||
| 				pcs.AddRange('[', '`'); |  | ||||||
| 				pcs.Add(@"|~"); |  | ||||||
| 				pcs.Remove(@"-_ "); |  | ||||||
| 				pcs.Remove(PwCharSet.Brackets); |  | ||||||
|  |  | ||||||
| 				m_strSpecial = pcs.ToString(); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Number of characters in this set. |         /// Number of characters in this set. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public uint Size |         public uint Size | ||||||
|         { |         { | ||||||
| 			get { return (uint)m_vChars.Count; } |             get { return (uint)m_lChars.Count; } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @@ -147,19 +106,39 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|         { |         { | ||||||
|             get |             get | ||||||
|             { |             { | ||||||
| 				if(uPos >= (uint)m_vChars.Count) |                 if (uPos >= (uint)m_lChars.Count) | ||||||
|                     throw new ArgumentOutOfRangeException("uPos"); |                     throw new ArgumentOutOfRangeException("uPos"); | ||||||
|  |  | ||||||
| 				return m_vChars[(int)uPos]; |                 return m_lChars[(int)uPos]; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public bool Equals(PwCharSet other) | ||||||
|  |         { | ||||||
|  |             if (object.ReferenceEquals(other, this)) return true; | ||||||
|  |             if (object.ReferenceEquals(other, null)) return false; | ||||||
|  |  | ||||||
|  |             if (m_lChars.Count != other.m_lChars.Count) return false; | ||||||
|  |  | ||||||
|  |             return MemUtil.ArraysEqual(m_vTab, other.m_vTab); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override bool Equals(object obj) | ||||||
|  |         { | ||||||
|  |             return Equals(obj as PwCharSet); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override int GetHashCode() | ||||||
|  |         { | ||||||
|  |             return (int)MemUtil.Hash32(m_vTab, 0, m_vTab.Length); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Remove all characters from this set. |         /// Remove all characters from this set. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public void Clear() |         public void Clear() | ||||||
|         { |         { | ||||||
| 			m_vChars.Clear(); |             m_lChars.Clear(); | ||||||
|             Array.Clear(m_vTab, 0, m_vTab.Length); |             Array.Clear(m_vTab, 0, m_vTab.Length); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -191,7 +170,7 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|  |  | ||||||
|             if (!Contains(ch)) |             if (!Contains(ch)) | ||||||
|             { |             { | ||||||
| 				m_vChars.Add(ch); |                 m_lChars.Add(ch); | ||||||
|                 m_vTab[ch / 8] |= (byte)(1 << (ch % 8)); |                 m_vTab[ch / 8] |= (byte)(1 << (ch % 8)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -205,8 +184,6 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|             Debug.Assert(strCharSet != null); |             Debug.Assert(strCharSet != null); | ||||||
|             if (strCharSet == null) throw new ArgumentNullException("strCharSet"); |             if (strCharSet == null) throw new ArgumentNullException("strCharSet"); | ||||||
|  |  | ||||||
| 			m_vChars.Capacity = m_vChars.Count + strCharSet.Length; |  | ||||||
|  |  | ||||||
|             foreach (char ch in strCharSet) |             foreach (char ch in strCharSet) | ||||||
|                 Add(ch); |                 Add(ch); | ||||||
|         } |         } | ||||||
| @@ -226,8 +203,6 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|  |  | ||||||
|         public void AddRange(char chMin, char chMax) |         public void AddRange(char chMin, char chMax) | ||||||
|         { |         { | ||||||
| 			m_vChars.Capacity = m_vChars.Count + (chMax - chMin) + 1; |  | ||||||
|  |  | ||||||
|             for (char ch = chMin; ch < chMax; ++ch) |             for (char ch = chMin; ch < chMax; ++ch) | ||||||
|                 Add(ch); |                 Add(ch); | ||||||
|  |  | ||||||
| @@ -241,11 +216,13 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|             switch (chCharSetIdentifier) |             switch (chCharSetIdentifier) | ||||||
|             { |             { | ||||||
|                 case 'a': Add(PwCharSet.LowerCase, PwCharSet.Digits); break; |                 case 'a': Add(PwCharSet.LowerCase, PwCharSet.Digits); break; | ||||||
| 				case 'A': Add(PwCharSet.LowerCase, PwCharSet.UpperCase, |                 case 'A': | ||||||
|  |                     Add(PwCharSet.LowerCase, PwCharSet.UpperCase, | ||||||
|                     PwCharSet.Digits); break; |                     PwCharSet.Digits); break; | ||||||
|                 case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break; |                 case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break; | ||||||
|                 case 'c': Add(PwCharSet.LowerConsonants); break; |                 case 'c': Add(PwCharSet.LowerConsonants); break; | ||||||
| 				case 'C': Add(PwCharSet.LowerConsonants, |                 case 'C': | ||||||
|  |                     Add(PwCharSet.LowerConsonants, | ||||||
|                     PwCharSet.UpperConsonants); break; |                     PwCharSet.UpperConsonants); break; | ||||||
|                 case 'z': Add(PwCharSet.UpperConsonants); break; |                 case 'z': Add(PwCharSet.UpperConsonants); break; | ||||||
|                 case 'd': Add(PwCharSet.Digits); break; // Digit |                 case 'd': Add(PwCharSet.Digits); break; // Digit | ||||||
| @@ -257,12 +234,13 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|                 case 'p': Add(PwCharSet.Punctuation); break; |                 case 'p': Add(PwCharSet.Punctuation); break; | ||||||
|                 case 'b': Add(PwCharSet.Brackets); break; |                 case 'b': Add(PwCharSet.Brackets); break; | ||||||
|                 case 's': Add(PwCharSet.PrintableAsciiSpecial); break; |                 case 's': Add(PwCharSet.PrintableAsciiSpecial); break; | ||||||
| 				case 'S': Add(PwCharSet.UpperCase, PwCharSet.LowerCase); |                 case 'S': | ||||||
|  |                     Add(PwCharSet.UpperCase, PwCharSet.LowerCase); | ||||||
|                     Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break; |                     Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break; | ||||||
|                 case 'v': Add(PwCharSet.LowerVowels); break; |                 case 'v': Add(PwCharSet.LowerVowels); break; | ||||||
|                 case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break; |                 case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break; | ||||||
|                 case 'Z': Add(PwCharSet.UpperVowels); break; |                 case 'Z': Add(PwCharSet.UpperVowels); break; | ||||||
| 				case 'x': Add(m_strHighAnsi); break; |                 case 'x': Add(PwCharSet.Latin1S); break; | ||||||
|                 default: bResult = false; break; |                 default: bResult = false; break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -272,7 +250,7 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|         public bool Remove(char ch) |         public bool Remove(char ch) | ||||||
|         { |         { | ||||||
|             m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8))); |             m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8))); | ||||||
| 			return m_vChars.Remove(ch); |             return m_lChars.Remove(ch); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public bool Remove(string strCharacters) |         public bool Remove(string strCharacters) | ||||||
| @@ -306,8 +284,8 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|         /// <returns>String containing all character set characters.</returns> |         /// <returns>String containing all character set characters.</returns> | ||||||
|         public override string ToString() |         public override string ToString() | ||||||
|         { |         { | ||||||
| 			StringBuilder sb = new StringBuilder(); |             StringBuilder sb = new StringBuilder(m_lChars.Count); | ||||||
| 			foreach(char ch in m_vChars) |             foreach (char ch in m_lChars) | ||||||
|                 sb.Append(ch); |                 sb.Append(ch); | ||||||
|  |  | ||||||
|             return sb.ToString(); |             return sb.ToString(); | ||||||
| @@ -320,13 +298,13 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|             sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_'); |             sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_'); | ||||||
|             sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_'); |             sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_'); | ||||||
|             sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_'); |             sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_'); | ||||||
| 			sb.Append(RemoveIfAllExist(m_strSpecial) ? 'S' : '_'); |             sb.Append(RemoveIfAllExist(PwCharSet.Special) ? 'S' : '_'); | ||||||
|             sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_'); |             sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_'); | ||||||
| 			sb.Append(RemoveIfAllExist(@"-") ? 'm' : '_'); |             sb.Append(RemoveIfAllExist("-") ? 'm' : '_'); | ||||||
| 			sb.Append(RemoveIfAllExist(@"_") ? 'u' : '_'); |             sb.Append(RemoveIfAllExist("_") ? 'u' : '_'); | ||||||
| 			sb.Append(RemoveIfAllExist(@" ") ? 's' : '_'); |             sb.Append(RemoveIfAllExist(" ") ? 's' : '_'); | ||||||
|             sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_'); |             sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_'); | ||||||
| 			sb.Append(RemoveIfAllExist(m_strHighAnsi) ? 'H' : '_'); |             sb.Append(RemoveIfAllExist(PwCharSet.Latin1S) ? 'H' : '_'); | ||||||
|  |  | ||||||
|             return sb.ToString(); |             return sb.ToString(); | ||||||
|         } |         } | ||||||
| @@ -339,13 +317,13 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|             if (strRanges[0] != '_') Add(PwCharSet.UpperCase); |             if (strRanges[0] != '_') Add(PwCharSet.UpperCase); | ||||||
|             if (strRanges[1] != '_') Add(PwCharSet.LowerCase); |             if (strRanges[1] != '_') Add(PwCharSet.LowerCase); | ||||||
|             if (strRanges[2] != '_') Add(PwCharSet.Digits); |             if (strRanges[2] != '_') Add(PwCharSet.Digits); | ||||||
| 			if(strRanges[3] != '_') Add(m_strSpecial); |             if (strRanges[3] != '_') Add(PwCharSet.Special); | ||||||
|             if (strRanges[4] != '_') Add(PwCharSet.Punctuation); |             if (strRanges[4] != '_') Add(PwCharSet.Punctuation); | ||||||
|             if (strRanges[5] != '_') Add('-'); |             if (strRanges[5] != '_') Add('-'); | ||||||
|             if (strRanges[6] != '_') Add('_'); |             if (strRanges[6] != '_') Add('_'); | ||||||
|             if (strRanges[7] != '_') Add(' '); |             if (strRanges[7] != '_') Add(' '); | ||||||
|             if (strRanges[8] != '_') Add(PwCharSet.Brackets); |             if (strRanges[8] != '_') Add(PwCharSet.Brackets); | ||||||
| 			if(strRanges[9] != '_') Add(m_strHighAnsi); |             if (strRanges[9] != '_') Add(PwCharSet.Latin1S); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
|   KeePass Password Safe - The Open-Source Password Manager |   KeePass Password Safe - The Open-Source Password Manager | ||||||
|   Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de> |   Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de> | ||||||
|  |  | ||||||
|   This program is free software; you can redistribute it and/or modify |   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 |   it under the terms of the GNU General Public License as published by | ||||||
| @@ -20,9 +20,13 @@ | |||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.Security.Cryptography; |  | ||||||
| using System.Text; | using System.Text; | ||||||
|  |  | ||||||
|  | #if !KeePassUAP | ||||||
|  | using System.Security.Cryptography; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | using KeePassLib.Resources; | ||||||
| using KeePassLib.Security; | using KeePassLib.Security; | ||||||
| using KeePassLib.Utility; | using KeePassLib.Utility; | ||||||
|  |  | ||||||
| @@ -33,95 +37,78 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|         Success = 0, |         Success = 0, | ||||||
|         Unknown = 1, |         Unknown = 1, | ||||||
|         TooFewCharacters = 2, |         TooFewCharacters = 2, | ||||||
| 		UnknownAlgorithm = 3 |         UnknownAlgorithm = 3, | ||||||
|  |         InvalidCharSet = 4, | ||||||
|  |         InvalidPattern = 5 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
| 	/// Utility functions for generating random passwords. |     /// Password generator. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public static class PwGenerator |     public static class PwGenerator | ||||||
|     { |     { | ||||||
| 		public static PwgError Generate(out ProtectedString psOut, |  | ||||||
| 			PwProfile pwProfile, byte[] pbUserEntropy, |         private static CryptoRandomStream CreateRandomStream(byte[] pbAdditionalEntropy, | ||||||
| 			CustomPwGeneratorPool pwAlgorithmPool) |             out byte[] pbKey) | ||||||
|         { |         { | ||||||
| 			Debug.Assert(pwProfile != null); |             pbKey = CryptoRandom.Instance.GetRandomBytes(128); | ||||||
| 			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 |             // Mix in additional entropy | ||||||
|             Debug.Assert(pbKey.Length >= 64); |             Debug.Assert(pbKey.Length >= 64); | ||||||
| 			if ((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0)) |             if ((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length != 0)) | ||||||
|             { |             { | ||||||
|                 using (SHA512Managed h = new SHA512Managed()) |                 using (SHA512Managed h = new SHA512Managed()) | ||||||
|                 { |                 { | ||||||
|                     byte[] pbHash = h.ComputeHash(pbAdditionalEntropy); |                     byte[] pbHash = h.ComputeHash(pbAdditionalEntropy); | ||||||
|                     MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length); |                     MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length); | ||||||
|  |                     MemUtil.ZeroByteArray(pbHash); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey); |             return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| 		internal static char GenerateCharacter(PwProfile pwProfile, |         internal static char GenerateCharacter(PwCharSet pwCharSet, | ||||||
| 			PwCharSet pwCharSet, CryptoRandomStream crsRandomSource) |             CryptoRandomStream crsRandomSource) | ||||||
|         { |         { | ||||||
| 			if (pwCharSet.Size == 0) return char.MinValue; |             uint cc = pwCharSet.Size; | ||||||
|  |             if (cc == 0) return char.MinValue; | ||||||
|  |  | ||||||
| 			ulong uIndex = crsRandomSource.GetRandomUInt64(); |             uint i = (uint)crsRandomSource.GetRandomUInt64(cc); | ||||||
| 			uIndex %= (ulong)pwCharSet.Size; |             return pwCharSet[i]; | ||||||
|  |  | ||||||
| 			char ch = pwCharSet[(uint)uIndex]; |  | ||||||
|  |  | ||||||
| 			if (pwProfile.NoRepeatingCharacters) |  | ||||||
| 				pwCharSet.Remove(ch); |  | ||||||
|  |  | ||||||
| 			return ch; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
| 		internal static void PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile) |         internal static bool PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile) | ||||||
|         { |         { | ||||||
| 			pwCharSet.Remove(PwCharSet.Invalid); |             uint cc = pwCharSet.Size; | ||||||
|  |             for (uint i = 0; i < cc; ++i) | ||||||
|  |             { | ||||||
|  |                 char ch = pwCharSet[i]; | ||||||
|  |                 if ((ch == char.MinValue) || (ch == '\t') || (ch == '\r') || | ||||||
|  |                     (ch == '\n') || char.IsSurrogate(ch)) | ||||||
|  |                     return false; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             if (pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike); |             if (pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike); | ||||||
|  |  | ||||||
| 			if (pwProfile.ExcludeCharacters.Length > 0) |             if (!string.IsNullOrEmpty(pwProfile.ExcludeCharacters)) | ||||||
|                 pwCharSet.Remove(pwProfile.ExcludeCharacters); |                 pwCharSet.Remove(pwProfile.ExcludeCharacters); | ||||||
|  |  | ||||||
|  |             return true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| 		internal static void ShufflePassword(char[] pPassword, |         internal static void Shuffle(char[] v, CryptoRandomStream crsRandomSource) | ||||||
| 			CryptoRandomStream crsRandomSource) |  | ||||||
|         { |         { | ||||||
| 			Debug.Assert(pPassword != null); if (pPassword == null) return; |             if (v == null) { Debug.Assert(false); return; } | ||||||
| 			Debug.Assert(crsRandomSource != null); if (crsRandomSource == null) return; |             if (crsRandomSource == null) { Debug.Assert(false); return; } | ||||||
|  |  | ||||||
| 			if (pPassword.Length <= 1) return; // Nothing to shuffle |             for (int i = v.Length - 1; i >= 1; --i) | ||||||
|  |  | ||||||
| 			for (int nSelect = 0; nSelect < pPassword.Length; ++nSelect) |  | ||||||
|             { |             { | ||||||
| 				ulong uRandomIndex = crsRandomSource.GetRandomUInt64(); |                 int j = (int)crsRandomSource.GetRandomUInt64((ulong)(i + 1)); | ||||||
| 				uRandomIndex %= (ulong)(pPassword.Length - nSelect); |  | ||||||
|  |  | ||||||
| 				char chTemp = pPassword[nSelect]; |                 char t = v[i]; | ||||||
| 				pPassword[nSelect] = pPassword[nSelect + (int)uRandomIndex]; |                 v[i] = v[j]; | ||||||
| 				pPassword[nSelect + (int)uRandomIndex] = chTemp; |                 v[j] = t; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -135,7 +122,7 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|             if (pwAlgorithmPool == null) return PwgError.UnknownAlgorithm; |             if (pwAlgorithmPool == null) return PwgError.UnknownAlgorithm; | ||||||
|  |  | ||||||
|             string strID = pwProfile.CustomAlgorithmUuid; |             string strID = pwProfile.CustomAlgorithmUuid; | ||||||
| 			if (string.IsNullOrEmpty(strID)) { Debug.Assert(false); return PwgError.UnknownAlgorithm; } |             if (string.IsNullOrEmpty(strID)) return PwgError.UnknownAlgorithm; | ||||||
|  |  | ||||||
|             byte[] pbUuid = Convert.FromBase64String(strID); |             byte[] pbUuid = Convert.FromBase64String(strID); | ||||||
|             PwUuid uuid = new PwUuid(pbUuid); |             PwUuid uuid = new PwUuid(pbUuid); | ||||||
| @@ -148,5 +135,57 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|             psOut = pwd; |             psOut = pwd; | ||||||
|             return PwgError.Success; |             return PwgError.Success; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         internal static string ErrorToString(PwgError e, bool bHeader) | ||||||
|  |         { | ||||||
|  |             if (e == PwgError.Success) { Debug.Assert(false); return string.Empty; } | ||||||
|  |             if ((e == PwgError.Unknown) && bHeader) return KLRes.PwGenFailed; | ||||||
|  |  | ||||||
|  |             string str = KLRes.UnknownError; | ||||||
|  |             switch (e) | ||||||
|  |             { | ||||||
|  |                 // case PwgError.Success: | ||||||
|  |                 //	break; | ||||||
|  |  | ||||||
|  |                 case PwgError.Unknown: | ||||||
|  |                     break; | ||||||
|  |  | ||||||
|  |                 case PwgError.TooFewCharacters: | ||||||
|  |                     str = KLRes.CharSetTooFewChars; | ||||||
|  |                     break; | ||||||
|  |  | ||||||
|  |                 case PwgError.UnknownAlgorithm: | ||||||
|  |                     str = KLRes.AlgorithmUnknown; | ||||||
|  |                     break; | ||||||
|  |  | ||||||
|  |                 case PwgError.InvalidCharSet: | ||||||
|  |                     str = KLRes.CharSetInvalid; | ||||||
|  |                     break; | ||||||
|  |  | ||||||
|  |                 case PwgError.InvalidPattern: | ||||||
|  |                     str = KLRes.PatternInvalid; | ||||||
|  |                     break; | ||||||
|  |  | ||||||
|  |                 default: | ||||||
|  |                     Debug.Assert(false); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (bHeader) | ||||||
|  |                 str = KLRes.PwGenFailed + MessageService.NewParagraph + str; | ||||||
|  |  | ||||||
|  |             return str; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         internal static string ErrorToString(Exception ex, bool bHeader) | ||||||
|  |         { | ||||||
|  |             string str = ((ex == null) ? KLRes.UnknownError : | ||||||
|  |                 StrUtil.FormatException(ex)); | ||||||
|  |  | ||||||
|  |             if (bHeader) | ||||||
|  |                 str = KLRes.PwGenFailed + MessageService.NewParagraph + str; | ||||||
|  |  | ||||||
|  |             return str; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
|   KeePass Password Safe - The Open-Source Password Manager |   KeePass Password Safe - The Open-Source Password Manager | ||||||
|   Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> |   Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de> | ||||||
|  |  | ||||||
|   This program is free software; you can redistribute it and/or modify |   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 |   it under the terms of the GNU General Public License as published by | ||||||
| @@ -19,8 +19,8 @@ | |||||||
|  |  | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Text; |  | ||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
| using KeePassLib.Utility; | using KeePassLib.Utility; | ||||||
|  |  | ||||||
| @@ -28,15 +28,17 @@ namespace KeePassLib.Cryptography | |||||||
| { | { | ||||||
|     public static class PopularPasswords |     public static class PopularPasswords | ||||||
|     { |     { | ||||||
| 		private static Dictionary<int, Dictionary<string, bool>> m_dicts = |         private static readonly Dictionary<int, Dictionary<char[], bool>> g_dicts = | ||||||
| 			new Dictionary<int, Dictionary<string, bool>>(); |             new Dictionary<int, Dictionary<char[], bool>>(); | ||||||
|  |  | ||||||
|         internal static int MaxLength |         internal static int MaxLength | ||||||
|         { |         { | ||||||
|             get |             get | ||||||
|             { |             { | ||||||
|  |                 Debug.Assert(g_dicts.Count > 0); // Should be initialized | ||||||
|  |  | ||||||
|                 int iMaxLen = 0; |                 int iMaxLen = 0; | ||||||
| 				foreach(int iLen in m_dicts.Keys) |                 foreach (int iLen in g_dicts.Keys) | ||||||
|                 { |                 { | ||||||
|                     if (iLen > iMaxLen) iMaxLen = iLen; |                     if (iLen > iMaxLen) iMaxLen = iLen; | ||||||
|                 } |                 } | ||||||
| @@ -47,8 +49,8 @@ namespace KeePassLib.Cryptography | |||||||
|  |  | ||||||
|         internal static bool ContainsLength(int nLength) |         internal static bool ContainsLength(int nLength) | ||||||
|         { |         { | ||||||
| 			Dictionary<string, bool> dDummy; |             Dictionary<char[], bool> dDummy; | ||||||
| 			return m_dicts.TryGetValue(nLength, out dDummy); |             return g_dicts.TryGetValue(nLength, out dDummy); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public static bool IsPopularPassword(char[] vPassword) |         public static bool IsPopularPassword(char[] vPassword) | ||||||
| @@ -62,28 +64,30 @@ namespace KeePassLib.Cryptography | |||||||
|             if (vPassword == null) throw new ArgumentNullException("vPassword"); |             if (vPassword == null) throw new ArgumentNullException("vPassword"); | ||||||
|             if (vPassword.Length == 0) { uDictSize = 0; return false; } |             if (vPassword.Length == 0) { uDictSize = 0; return false; } | ||||||
|  |  | ||||||
| 			string str = new string(vPassword); | #if DEBUG | ||||||
|  |             Array.ForEach(vPassword, ch => Debug.Assert(ch == char.ToLower(ch))); | ||||||
|  | #endif | ||||||
|  |  | ||||||
| 			try { return IsPopularPasswordPriv(str, out uDictSize); } |             try { return IsPopularPasswordPriv(vPassword, out uDictSize); } | ||||||
|             catch (Exception) { Debug.Assert(false); } |             catch (Exception) { Debug.Assert(false); } | ||||||
|  |  | ||||||
|             uDictSize = 0; |             uDictSize = 0; | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| 		private static bool IsPopularPasswordPriv(string str, out ulong uDictSize) |         private static bool IsPopularPasswordPriv(char[] vPassword, out ulong uDictSize) | ||||||
|         { |         { | ||||||
| 			Debug.Assert(m_dicts.Count > 0); // Should be initialized with data |             Debug.Assert(g_dicts.Count > 0); // Should be initialized with data | ||||||
|  |  | ||||||
| 			Dictionary<string, bool> d; |             Dictionary<char[], bool> d; | ||||||
| 			if(!m_dicts.TryGetValue(str.Length, out d)) |             if (!g_dicts.TryGetValue(vPassword.Length, out d)) | ||||||
|             { |             { | ||||||
|                 uDictSize = 0; |                 uDictSize = 0; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             uDictSize = (ulong)d.Count; |             uDictSize = (ulong)d.Count; | ||||||
| 			return d.ContainsKey(str); |             return d.ContainsKey(vPassword); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public static void Add(byte[] pbData, bool bGZipped) |         public static void Add(byte[] pbData, bool bGZipped) | ||||||
| @@ -96,30 +100,27 @@ namespace KeePassLib.Cryptography | |||||||
|                 string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length); |                 string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length); | ||||||
|                 if (string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; } |                 if (string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; } | ||||||
|  |  | ||||||
| 				if(!char.IsWhiteSpace(strData[strData.Length - 1])) |  | ||||||
| 					strData += "\n"; |  | ||||||
|  |  | ||||||
|                 StringBuilder sb = new StringBuilder(); |                 StringBuilder sb = new StringBuilder(); | ||||||
| 				for(int i = 0; i < strData.Length; ++i) |                 for (int i = 0; i <= strData.Length; ++i) | ||||||
|                 { |                 { | ||||||
| 					char ch = strData[i]; |                     char ch = ((i == strData.Length) ? ' ' : strData[i]); | ||||||
|  |  | ||||||
|                     if (char.IsWhiteSpace(ch)) |                     if (char.IsWhiteSpace(ch)) | ||||||
|                     { |                     { | ||||||
|                         int cc = sb.Length; |                         int cc = sb.Length; | ||||||
|                         if (cc > 0) |                         if (cc > 0) | ||||||
|                         { |                         { | ||||||
| 							string strWord = sb.ToString(); |                             char[] vWord = new char[cc]; | ||||||
| 							Debug.Assert(strWord.Length == cc); |                             sb.CopyTo(0, vWord, 0, cc); | ||||||
|  |  | ||||||
| 							Dictionary<string, bool> d; |                             Dictionary<char[], bool> d; | ||||||
| 							if(!m_dicts.TryGetValue(cc, out d)) |                             if (!g_dicts.TryGetValue(cc, out d)) | ||||||
|                             { |                             { | ||||||
| 								d = new Dictionary<string, bool>(); |                                 d = new Dictionary<char[], bool>(MemUtil.ArrayHelperExOfChar); | ||||||
| 								m_dicts[cc] = d; |                                 g_dicts[cc] = d; | ||||||
|                             } |                             } | ||||||
|  |  | ||||||
| 							d[strWord] = true; |                             d[vWord] = true; | ||||||
|                             sb.Remove(0, cc); |                             sb.Remove(0, cc); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
|   KeePass Password Safe - The Open-Source Password Manager |   KeePass Password Safe - The Open-Source Password Manager | ||||||
|   Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> |   Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de> | ||||||
|  |  | ||||||
|   This program is free software; you can redistribute it and/or modify |   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 |   it under the terms of the GNU General Public License as published by | ||||||
| @@ -19,8 +19,8 @@ | |||||||
|  |  | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Text; |  | ||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
| using KeePassLib.Cryptography.PasswordGenerator; | using KeePassLib.Cryptography.PasswordGenerator; | ||||||
| using KeePassLib.Utility; | using KeePassLib.Utility; | ||||||
| @@ -35,19 +35,19 @@ namespace KeePassLib.Cryptography | |||||||
|     { |     { | ||||||
|         private static class PatternID |         private static class PatternID | ||||||
|         { |         { | ||||||
| 			public const char LowerAlpha = 'L'; |             internal const char LowerAlpha = 'L'; | ||||||
| 			public const char UpperAlpha = 'U'; |             internal const char UpperAlpha = 'U'; | ||||||
| 			public const char Digit = 'D'; |             internal const char Digit = 'D'; | ||||||
| 			public const char Special = 'S'; |             internal const char Special = 'S'; | ||||||
| 			public const char High = 'H'; |             internal const char Latin1S = 'H'; | ||||||
| 			public const char Other = 'X'; |             internal const char Other = 'X'; | ||||||
|  |  | ||||||
| 			public const char Dictionary = 'W'; |             internal const char Dictionary = 'W'; | ||||||
| 			public const char Repetition = 'R'; |             internal const char Repetition = 'R'; | ||||||
| 			public const char Number = 'N'; |             internal const char Number = 'N'; | ||||||
| 			public const char DiffSeq = 'C'; |             internal const char DiffSeq = 'C'; | ||||||
|  |  | ||||||
| 			public const string All = "LUDSHXWRNC"; |             internal const string All = "LUDSHXWRNC"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // private static class CharDistrib |         // private static class CharDistrib | ||||||
| @@ -125,7 +125,7 @@ namespace KeePassLib.Cryptography | |||||||
|         private sealed class EntropyEncoder |         private sealed class EntropyEncoder | ||||||
|         { |         { | ||||||
|             private readonly string m_strAlph; |             private readonly string m_strAlph; | ||||||
| 			private Dictionary<char, ulong> m_dHisto = new Dictionary<char, ulong>(); |             private readonly Dictionary<char, ulong> m_dHisto = new Dictionary<char, ulong>(); | ||||||
|             private readonly ulong m_uBaseWeight; |             private readonly ulong m_uBaseWeight; | ||||||
|             private readonly ulong m_uCharWeight; |             private readonly ulong m_uCharWeight; | ||||||
|             private readonly ulong m_uOccExclThreshold; |             private readonly ulong m_uOccExclThreshold; | ||||||
| @@ -189,7 +189,7 @@ namespace KeePassLib.Cryptography | |||||||
|  |  | ||||||
|         private sealed class MultiEntropyEncoder |         private sealed class MultiEntropyEncoder | ||||||
|         { |         { | ||||||
| 			private Dictionary<char, EntropyEncoder> m_dEncs = |             private readonly Dictionary<char, EntropyEncoder> m_dEncs = | ||||||
|                 new Dictionary<char, EntropyEncoder>(); |                 new Dictionary<char, EntropyEncoder>(); | ||||||
|  |  | ||||||
|             public MultiEntropyEncoder() |             public MultiEntropyEncoder() | ||||||
| @@ -281,7 +281,7 @@ namespace KeePassLib.Cryptography | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| 		private static object m_objSyncInit = new object(); |         private static readonly object m_objSyncInit = new object(); | ||||||
|         private static List<QeCharType> m_lCharTypes = null; |         private static List<QeCharType> m_lCharTypes = null; | ||||||
|  |  | ||||||
|         private static void EnsureInitialized() |         private static void EnsureInitialized() | ||||||
| @@ -292,25 +292,20 @@ namespace KeePassLib.Cryptography | |||||||
|                 { |                 { | ||||||
|                     string strSpecial = PwCharSet.PrintableAsciiSpecial; |                     string strSpecial = PwCharSet.PrintableAsciiSpecial; | ||||||
|                     if (strSpecial.IndexOf(' ') >= 0) { Debug.Assert(false); } |                     if (strSpecial.IndexOf(' ') >= 0) { Debug.Assert(false); } | ||||||
| 					else strSpecial = strSpecial + " "; |                     else strSpecial += " "; | ||||||
|  |  | ||||||
|                     int nSp = strSpecial.Length; |                     int nSp = strSpecial.Length; | ||||||
| 					int nHi = PwCharSet.HighAnsiChars.Length; |                     int nL1S = PwCharSet.Latin1S.Length; | ||||||
|  |  | ||||||
| 					m_lCharTypes = new List<QeCharType>(); |                     m_lCharTypes = new List<QeCharType>() | ||||||
|  |                     { | ||||||
| 					m_lCharTypes.Add(new QeCharType(PatternID.LowerAlpha, |                         new QeCharType(PatternID.LowerAlpha, PwCharSet.LowerCase, true), | ||||||
| 						PwCharSet.LowerCase, true)); |                         new QeCharType(PatternID.UpperAlpha, PwCharSet.UpperCase, true), | ||||||
| 					m_lCharTypes.Add(new QeCharType(PatternID.UpperAlpha, |                         new QeCharType(PatternID.Digit, PwCharSet.Digits, true), | ||||||
| 						PwCharSet.UpperCase, true)); |                         new QeCharType(PatternID.Special, strSpecial, false), | ||||||
| 					m_lCharTypes.Add(new QeCharType(PatternID.Digit, |                         new QeCharType(PatternID.Latin1S, PwCharSet.Latin1S, false), | ||||||
| 						PwCharSet.Digits, true)); |                         new QeCharType(PatternID.Other, 0x10000 - (2 * 26) - 10 - nSp - nL1S) | ||||||
| 					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)); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -318,30 +313,30 @@ namespace KeePassLib.Cryptography | |||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Estimate the quality of a password. |         /// Estimate the quality of a password. | ||||||
|         /// </summary> |         /// </summary> | ||||||
| 		/// <param name="vPasswordChars">Password to check.</param> |         /// <param name="vPassword">Password to check.</param> | ||||||
|         /// <returns>Estimated bit-strength of the password.</returns> |         /// <returns>Estimated bit-strength of the password.</returns> | ||||||
| 		public static uint EstimatePasswordBits(char[] vPasswordChars) |         public static uint EstimatePasswordBits(char[] vPassword) | ||||||
|         { |         { | ||||||
| 			if(vPasswordChars == null) { Debug.Assert(false); return 0; } |             if (vPassword == null) { Debug.Assert(false); return 0; } | ||||||
| 			if(vPasswordChars.Length == 0) return 0; |             if (vPassword.Length == 0) return 0; | ||||||
|  |  | ||||||
|             EnsureInitialized(); |             EnsureInitialized(); | ||||||
|  |  | ||||||
| 			int n = vPasswordChars.Length; |             int n = vPassword.Length; | ||||||
|             List<QePatternInstance>[] vPatterns = new List<QePatternInstance>[n]; |             List<QePatternInstance>[] vPatterns = new List<QePatternInstance>[n]; | ||||||
|             for (int i = 0; i < n; ++i) |             for (int i = 0; i < n; ++i) | ||||||
|             { |             { | ||||||
|                 vPatterns[i] = new List<QePatternInstance>(); |                 vPatterns[i] = new List<QePatternInstance>(); | ||||||
|  |  | ||||||
|                 QePatternInstance piChar = new QePatternInstance(i, 1, |                 QePatternInstance piChar = new QePatternInstance(i, 1, | ||||||
| 					GetCharType(vPasswordChars[i])); |                     GetCharType(vPassword[i])); | ||||||
|                 vPatterns[i].Add(piChar); |                 vPatterns[i].Add(piChar); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| 			FindRepetitions(vPasswordChars, vPatterns); |             FindRepetitions(vPassword, vPatterns); | ||||||
| 			FindNumbers(vPasswordChars, vPatterns); |             FindNumbers(vPassword, vPatterns); | ||||||
| 			FindDiffSeqs(vPasswordChars, vPatterns); |             FindDiffSeqs(vPassword, vPatterns); | ||||||
| 			FindPopularPasswords(vPasswordChars, vPatterns); |             FindPopularPasswords(vPassword, vPatterns); | ||||||
|  |  | ||||||
|             // Encoders must not be static, because the entropy estimation |             // Encoders must not be static, because the entropy estimation | ||||||
|             // may run concurrently in multiple threads and the encoders are |             // may run concurrently in multiple threads and the encoders are | ||||||
| @@ -382,7 +377,7 @@ namespace KeePassLib.Cryptography | |||||||
|                 { |                 { | ||||||
|                     Debug.Assert(s.Position == n); |                     Debug.Assert(s.Position == n); | ||||||
|  |  | ||||||
| 					double dblCost = ComputePathCost(s.Path, vPasswordChars, |                     double dblCost = ComputePathCost(s.Path, vPassword, | ||||||
|                         ecPattern, mcData); |                         ecPattern, mcData); | ||||||
|                     if (dblCost < dblMinCost) dblMinCost = dblCost; |                     if (dblCost < dblMinCost) dblMinCost = dblCost; | ||||||
|                 } |                 } | ||||||
| @@ -420,11 +415,12 @@ namespace KeePassLib.Cryptography | |||||||
|         { |         { | ||||||
|             if (pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; } |             if (pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; } | ||||||
|  |  | ||||||
| 			char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8); |             char[] v = StrUtil.Utf8.GetChars(pbUnprotectedUtf8); | ||||||
| 			uint uResult = EstimatePasswordBits(vChars); |             uint r; | ||||||
| 			MemUtil.ZeroArray<char>(vChars); |             try { r = EstimatePasswordBits(v); } | ||||||
|  |             finally { MemUtil.ZeroArray<char>(v); } | ||||||
|  |  | ||||||
| 			return uResult; |             return r; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private static QeCharType GetCharType(char ch) |         private static QeCharType GetCharType(char ch) | ||||||
| @@ -482,7 +478,7 @@ namespace KeePassLib.Cryptography | |||||||
|                 vLeet[i] = char.ToLower(DecodeLeetChar(ch)); |                 vLeet[i] = char.ToLower(DecodeLeetChar(ch)); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| 			char chErased = default(char); |             char chErased = default(char); // The value that Array.Clear uses | ||||||
|             Debug.Assert(chErased == char.MinValue); |             Debug.Assert(chErased == char.MinValue); | ||||||
|  |  | ||||||
|             int nMaxLen = Math.Min(n, PopularPasswords.MaxLength); |             int nMaxLen = Math.Min(n, PopularPasswords.MaxLength); | ||||||
| @@ -515,7 +511,12 @@ namespace KeePassLib.Cryptography | |||||||
|                         Debug.Assert(vLower[i] == chErased); |                         Debug.Assert(vLower[i] == chErased); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |                 MemUtil.ZeroArray<char>(vSub); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             MemUtil.ZeroArray<char>(vLower); | ||||||
|  |             MemUtil.ZeroArray<char>(vLeet); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private static bool EvalAddPopularPasswordPattern(List<QePatternInstance>[] vPatterns, |         private static bool EvalAddPopularPasswordPattern(List<QePatternInstance>[] vPatterns, | ||||||
| @@ -659,6 +660,8 @@ namespace KeePassLib.Cryptography | |||||||
|                     if (bFoundRep) ErasePart(v, x1, m, ref chErased); |                     if (bFoundRep) ErasePart(v, x1, m, ref chErased); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             MemUtil.ZeroArray<char>(v); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private static bool PartsEqual(char[] v, int x1, int x2, int nLength) |         private static bool PartsEqual(char[] v, int x1, int x2, int nLength) | ||||||
| @@ -685,23 +688,25 @@ namespace KeePassLib.Cryptography | |||||||
|         { |         { | ||||||
|             int n = vPassword.Length; |             int n = vPassword.Length; | ||||||
|             StringBuilder sb = new StringBuilder(); |             StringBuilder sb = new StringBuilder(); | ||||||
|  |  | ||||||
|             for (int i = 0; i < n; ++i) |             for (int i = 0; i < n; ++i) | ||||||
|             { |             { | ||||||
|                 char ch = vPassword[i]; |                 char ch = vPassword[i]; | ||||||
|                 if ((ch >= '0') && (ch <= '9')) sb.Append(ch); |                 if ((ch >= '0') && (ch <= '9')) sb.Append(ch); | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
| 					AddNumberPattern(vPatterns, sb.ToString(), i - sb.Length); |                     AddNumberPattern(vPatterns, sb, i - sb.Length); | ||||||
|                     sb.Remove(0, sb.Length); |                     sb.Remove(0, sb.Length); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 			AddNumberPattern(vPatterns, sb.ToString(), n - sb.Length); |             AddNumberPattern(vPatterns, sb, n - sb.Length); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private static void AddNumberPattern(List<QePatternInstance>[] vPatterns, |         private static void AddNumberPattern(List<QePatternInstance>[] vPatterns, | ||||||
| 			string strNumber, int i) |             StringBuilder sb, int i) | ||||||
|         { |         { | ||||||
| 			if(strNumber.Length <= 2) return; |             if (sb.Length <= 2) return; | ||||||
|  |             string strNumber = sb.ToString(); | ||||||
|  |  | ||||||
|             int nZeros = 0; |             int nZeros = 0; | ||||||
|             for (int j = 0; j < strNumber.Length; ++j) |             for (int j = 0; j < strNumber.Length; ++j) | ||||||
| @@ -733,17 +738,18 @@ namespace KeePassLib.Cryptography | |||||||
|         private static void FindDiffSeqs(char[] vPassword, |         private static void FindDiffSeqs(char[] vPassword, | ||||||
|             List<QePatternInstance>[] vPatterns) |             List<QePatternInstance>[] vPatterns) | ||||||
|         { |         { | ||||||
| 			int d = int.MinValue, p = 0; |             int n = vPassword.Length; | ||||||
| 			string str = new string(vPassword) + new string(char.MaxValue, 1); |             int d = int.MaxValue, p = 0; | ||||||
|  |  | ||||||
| 			for(int i = 1; i < str.Length; ++i) |             for (int i = 1; i <= n; ++i) | ||||||
|             { |             { | ||||||
| 				int dCur = (int)str[i] - (int)str[i - 1]; |                 int dCur = ((i == n) ? int.MinValue : | ||||||
|  |                     ((int)vPassword[i] - (int)vPassword[i - 1])); | ||||||
|                 if (dCur != d) |                 if (dCur != d) | ||||||
|                 { |                 { | ||||||
|                     if ((i - p) >= 3) // At least 3 chars involved |                     if ((i - p) >= 3) // At least 3 chars involved | ||||||
|                     { |                     { | ||||||
| 						QeCharType ct = GetCharType(str[p]); |                         QeCharType ct = GetCharType(vPassword[p]); | ||||||
|                         double dblCost = ct.CharSize + Log2(i - p - 1); |                         double dblCost = ct.CharSize + Log2(i - p - 1); | ||||||
|  |  | ||||||
|                         vPatterns[p].Add(new QePatternInstance(p, |                         vPatterns[p].Add(new QePatternInstance(p, | ||||||
|   | |||||||
| @@ -46,4 +46,12 @@ namespace KeePassLib.Delegates | |||||||
| 	public delegate void VoidDelegate(); | 	public delegate void VoidDelegate(); | ||||||
|  |  | ||||||
| 	public delegate string StrPwEntryDelegate(string str, PwEntry pe); | 	public delegate string StrPwEntryDelegate(string str, PwEntry pe); | ||||||
|  |  | ||||||
|  |     public delegate TResult GFunc<TResult>(); | ||||||
|  |     public delegate TResult GFunc<T, TResult>(T o); | ||||||
|  |     public delegate TResult GFunc<T1, T2, TResult>(T1 o1, T2 o2); | ||||||
|  |     public delegate TResult GFunc<T1, T2, T3, TResult>(T1 o1, T2 o2, T3 o3); | ||||||
|  |     public delegate TResult GFunc<T1, T2, T3, T4, TResult>(T1 o1, T2 o2, T3 o3, T4 o4); | ||||||
|  |     public delegate TResult GFunc<T1, T2, T3, T4, T5, TResult>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5); | ||||||
|  |     public delegate TResult GFunc<T1, T2, T3, T4, T5, T6, TResult>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5, T6 o6); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
|   KeePass Password Safe - The Open-Source Password Manager |   KeePass Password Safe - The Open-Source Password Manager | ||||||
|   Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> |   Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de> | ||||||
|  |  | ||||||
|   This program is free software; you can redistribute it and/or modify |   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 |   it under the terms of the GNU General Public License as published by | ||||||
| @@ -22,6 +22,7 @@ using System.Collections.Generic; | |||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.IO; | using System.IO; | ||||||
| using System.Runtime.CompilerServices; | using System.Runtime.CompilerServices; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
| using System.Text; | using System.Text; | ||||||
|  |  | ||||||
| #if KeePassLibSD | #if KeePassLibSD | ||||||
| @@ -30,81 +31,26 @@ using KeePassLibSD; | |||||||
| using System.IO.Compression; | using System.IO.Compression; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | using KeePassLib.Delegates; | ||||||
|  |  | ||||||
| namespace KeePassLib.Utility | namespace KeePassLib.Utility | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
| 	/// Contains static buffer manipulation and string conversion routines. |     /// Buffer manipulation and conversion routines. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public static class MemUtil |     public static class MemUtil | ||||||
|     { |     { | ||||||
|         public static readonly byte[] EmptyByteArray = new byte[0]; |         public static readonly byte[] EmptyByteArray = new byte[0]; | ||||||
|  |  | ||||||
| 		private static readonly uint[] m_vSBox = new uint[256] { |         internal static readonly ArrayHelperEx<char> ArrayHelperExOfChar = | ||||||
| 			0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230, |             new ArrayHelperEx<char>(); | ||||||
| 			0x3A321712, 0x06403DB1, 0xD2F84B95, 0xDF22A6E4, |  | ||||||
| 			0x07CE9E5B, 0x31788A0C, 0xF683F6F4, 0xEA061F49, |         private const MethodImplOptions MioNoOptimize = | ||||||
| 			0xFA5C2ACA, 0x4B9E494E, 0xB0AB25BA, 0x767731FC, | #if KeePassLibSD | ||||||
| 			0x261893A7, 0x2B09F2CE, 0x046261E4, 0x41367B4B, | 			MethodImplOptions.NoInlining; | ||||||
| 			0x18A7F225, 0x8F923C0E, 0x5EF3A325, 0x28D0435E, | #else | ||||||
| 			0x84C22919, 0xED66873C, 0x8CEDE444, 0x7FC47C24, |             (MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining); | ||||||
| 			0xFCFC6BA3, 0x676F928D, 0xB4147187, 0xD8FB126E, | #endif | ||||||
| 			0x7D798D17, 0xFF82E424, 0x1712FA5B, 0xABB09DD5, |  | ||||||
| 			0x8156BA63, 0x84E4D969, 0xC937FB9A, 0x2F1E5BFC, |  | ||||||
| 			0x178ECA11, 0x0E71CD5F, 0x52AAC6F4, 0x71EEFC8F, |  | ||||||
| 			0x7090D749, 0x21CACA31, 0x92996378, 0x0939A8A8, |  | ||||||
| 			0xE9EE1934, 0xD2718616, 0xF2500543, 0xB911873C, |  | ||||||
| 			0xD3CB3EEC, 0x2BA0DBEB, 0xB42D0A27, 0xECE67C0F, |  | ||||||
| 			0x302925F0, 0x6114F839, 0xD39E6307, 0xE28970D6, |  | ||||||
| 			0xEB982F99, 0x941B4CDF, 0xC540E550, 0x8124FC45, |  | ||||||
| 			0x98B025C7, 0xE2BF90EA, 0x4F57C976, 0xCF546FE4, |  | ||||||
| 			0x59566DC8, 0xE3F4360D, 0xF5F9D231, 0xD6180B22, |  | ||||||
| 			0xB54E088A, 0xB5DFE6A6, 0x3637A36F, 0x056E9284, |  | ||||||
| 			0xAFF8FBC5, 0x19E01648, 0x8611F043, 0xDAE44337, |  | ||||||
| 			0xF61B6A1C, 0x257ACD9E, 0xDD35F507, 0xEF05CAFA, |  | ||||||
| 			0x05EB4A83, 0xFC25CA92, 0x0A4728E6, 0x9CF150EF, |  | ||||||
| 			0xAEEF67DE, 0xA9472337, 0x57C81EFE, 0x3E5E009F, |  | ||||||
| 			0x02CB03BB, 0x2BA85674, 0xF21DC251, 0x78C34A34, |  | ||||||
| 			0xABB1F5BF, 0xB95A2FBD, 0x1FB47777, 0x9A96E8AC, |  | ||||||
| 			0x5D2D2838, 0x55AAC92A, 0x99EE324E, 0x10F6214B, |  | ||||||
| 			0x58ABDFB1, 0x2008794D, 0xBEC880F0, 0xE75E5341, |  | ||||||
| 			0x88015C34, 0x352D8FBF, 0x622B7F6C, 0xF5C59EA2, |  | ||||||
| 			0x1F759D8E, 0xADE56159, 0xCC7B4C25, 0x5B8BC48C, |  | ||||||
| 			0xB6BD15AF, 0x3C5B5110, 0xE74A7C3D, 0xEE613161, |  | ||||||
| 			0x156A1C67, 0x72C06817, 0xEA0A6F69, 0x4CECF993, |  | ||||||
| 			0xCA9D554C, 0x8E20361F, 0x42D396B9, 0x595DE578, |  | ||||||
| 			0x749D7955, 0xFD1BA5FD, 0x81FC160E, 0xDB97E28C, |  | ||||||
| 			0x7CF148F7, 0x0B0B3CF5, 0x534DE605, 0x46421066, |  | ||||||
| 			0xD4B68DD1, 0x9E479CE6, 0xAE667A9D, 0xBC082082, |  | ||||||
| 			0xB06DD6EF, 0x20F0F23F, 0xB99E1551, 0xF47A2E3A, |  | ||||||
| 			0x71DA50C6, 0x67B65779, 0x2A8CB376, 0x1EA71EEE, |  | ||||||
| 			0x29ABCD50, 0xB6EB0C6B, 0x23C10511, 0x6F3F2144, |  | ||||||
| 			0x6AF23012, 0xF696BD9E, 0xB94099D8, 0xAD5A9C81, |  | ||||||
| 			0x7A0794FA, 0x7EDF59D6, 0x1E72E574, 0x8561913C, |  | ||||||
| 			0x4E4D568F, 0xEECB9928, 0x9C124D2E, 0x0848B82C, |  | ||||||
| 			0xF1CA395F, 0x9DAF43DC, 0xF77EC323, 0x394E9B59, |  | ||||||
| 			0x7E200946, 0x8B811D68, 0x16DA3305, 0xAB8DE2C3, |  | ||||||
| 			0xE6C53B64, 0x98C2D321, 0x88A97D81, 0xA7106419, |  | ||||||
| 			0x8E52F7BF, 0x8ED262AF, 0x7CCA974E, 0xF0933241, |  | ||||||
| 			0x040DD437, 0xE143B3D4, 0x3019F56F, 0xB741521D, |  | ||||||
| 			0xF1745362, 0x4C435F9F, 0xB4214D0D, 0x0B0C348B, |  | ||||||
| 			0x5051D189, 0x4C30447E, 0x7393D722, 0x95CEDD0B, |  | ||||||
| 			0xDD994E80, 0xC3D22ED9, 0x739CD900, 0x131EB9C4, |  | ||||||
| 			0xEF1062B2, 0x4F0DE436, 0x52920073, 0x9A7F3D80, |  | ||||||
| 			0x896E7B1B, 0x2C8BBE5A, 0xBD304F8A, 0xA993E22C, |  | ||||||
| 			0x134C41A0, 0xFA989E00, 0x39CE9726, 0xFB89FCCF, |  | ||||||
| 			0xE8FBAC97, 0xD4063FFC, 0x935A2B5A, 0x44C8EE83, |  | ||||||
| 			0xCB2BC7B6, 0x02989E92, 0x75478BEA, 0x144378D0, |  | ||||||
| 			0xD853C087, 0x8897A34E, 0xDD23629D, 0xBDE2A2A2, |  | ||||||
| 			0x581D8ECC, 0x5DA8AEE8, 0xFF8AAFD0, 0xBA2BCF6E, |  | ||||||
| 			0x4BD98DAC, 0xF2EDB9E4, 0xFA2DC868, 0x47E84661, |  | ||||||
| 			0xECEB1C7D, 0x41705CA4, 0x5982E4D4, 0xEB5204A1, |  | ||||||
| 			0xD196CAFB, 0x6414804D, 0x3ABD4B46, 0x8B494C26, |  | ||||||
| 			0xB432D52B, 0x39C5356B, 0x6EC80BF7, 0x71BE5483, |  | ||||||
| 			0xCEC4A509, 0xE9411D61, 0x52F341E5, 0xD2E6197B, |  | ||||||
| 			0x4F02826C, 0xA9E48838, 0xD1F8F247, 0xE4957FB3, |  | ||||||
| 			0x586CCA99, 0x9A8B6A5B, 0x4998FBEA, 0xF762BE4C, |  | ||||||
| 			0x90DFE33C, 0x9731511E, 0x88C6A82F, 0xDD65A4D4 |  | ||||||
| 		}; |  | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Convert a hexadecimal string to a byte array. The input string must be |         /// Convert a hexadecimal string to a byte array. The input string must be | ||||||
| @@ -143,11 +89,11 @@ namespace KeePassLib.Utility | |||||||
|  |  | ||||||
|                 ch = strHex[i + 1]; |                 ch = strHex[i + 1]; | ||||||
|                 if ((ch >= '0') && (ch <= '9')) |                 if ((ch >= '0') && (ch <= '9')) | ||||||
| 					bt += (byte)(ch - '0'); |                     bt |= (byte)(ch - '0'); | ||||||
|                 else if ((ch >= 'a') && (ch <= 'f')) |                 else if ((ch >= 'a') && (ch <= 'f')) | ||||||
| 					bt += (byte)(ch - 'a' + 10); |                     bt |= (byte)(ch - 'a' + 10); | ||||||
|                 else if ((ch >= 'A') && (ch <= 'F')) |                 else if ((ch >= 'A') && (ch <= 'F')) | ||||||
| 					bt += (byte)(ch - 'A' + 10); |                     bt |= (byte)(ch - 'A' + 10); | ||||||
|                 else { Debug.Assert(false); } |                 else { Debug.Assert(false); } | ||||||
|  |  | ||||||
|                 pb[i >> 1] = bt; |                 pb[i >> 1] = bt; | ||||||
| @@ -246,20 +192,26 @@ namespace KeePassLib.Utility | |||||||
|             return l.ToArray(); |             return l.ToArray(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         internal static byte[] ParseBase32(string str, bool bAutoPad) | ||||||
|  |         { | ||||||
|  |             if (str == null) { Debug.Assert(false); return null; } | ||||||
|  |  | ||||||
|  |             // https://sourceforge.net/p/keepass/discussion/329220/thread/59b61fddea/ | ||||||
|  |             if (bAutoPad && ((str.Length % 8) != 0)) | ||||||
|  |                 str = str.PadRight((str.Length & ~7) + 8, '='); | ||||||
|  |  | ||||||
|  |             return ParseBase32(str); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Set all bytes in a byte array to zero. |         /// Set all bytes in a byte array to zero. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="pbArray">Input array. All bytes of this array |         /// <param name="pbArray">Input array. All bytes of this array | ||||||
|         /// will be set to zero.</param> |         /// will be set to zero.</param> | ||||||
| #if KeePassLibSD |         [MethodImpl(MioNoOptimize)] | ||||||
| 		[MethodImpl(MethodImplOptions.NoInlining)] |  | ||||||
| #else |  | ||||||
| 		[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] |  | ||||||
| #endif |  | ||||||
|         public static void ZeroByteArray(byte[] pbArray) |         public static void ZeroByteArray(byte[] pbArray) | ||||||
|         { |         { | ||||||
| 			Debug.Assert(pbArray != null); |             if (pbArray == null) { Debug.Assert(false); return; } | ||||||
| 			if(pbArray == null) throw new ArgumentNullException("pbArray"); |  | ||||||
|  |  | ||||||
|             Array.Clear(pbArray, 0, pbArray.Length); |             Array.Clear(pbArray, 0, pbArray.Length); | ||||||
|         } |         } | ||||||
| @@ -268,18 +220,41 @@ namespace KeePassLib.Utility | |||||||
|         /// Set all elements of an array to the default value. |         /// Set all elements of an array to the default value. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="v">Input array.</param> |         /// <param name="v">Input array.</param> | ||||||
| #if KeePassLibSD |         [MethodImpl(MioNoOptimize)] | ||||||
| 		[MethodImpl(MethodImplOptions.NoInlining)] |  | ||||||
| #else |  | ||||||
| 		[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] |  | ||||||
| #endif |  | ||||||
|         public static void ZeroArray<T>(T[] v) |         public static void ZeroArray<T>(T[] v) | ||||||
|         { |         { | ||||||
| 			if(v == null) { Debug.Assert(false); throw new ArgumentNullException("v"); } |             if (v == null) { Debug.Assert(false); return; } | ||||||
|  |  | ||||||
|             Array.Clear(v, 0, v.Length); |             Array.Clear(v, 0, v.Length); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         private static byte[] g_pbZero = null; | ||||||
|  |         [MethodImpl(MioNoOptimize)] | ||||||
|  |         public static void ZeroMemory(IntPtr pb, long cb) | ||||||
|  |         { | ||||||
|  |             if (pb == IntPtr.Zero) { Debug.Assert(false); return; } | ||||||
|  |             if (cb < 0) { Debug.Assert(false); return; } | ||||||
|  |  | ||||||
|  |             byte[] pbZero = g_pbZero; | ||||||
|  |             if (pbZero == null) | ||||||
|  |             { | ||||||
|  |                 pbZero = new byte[4096]; | ||||||
|  |                 g_pbZero = pbZero; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             long cbZero = pbZero.Length; | ||||||
|  |  | ||||||
|  |             while (cb != 0) | ||||||
|  |             { | ||||||
|  |                 long cbBlock = Math.Min(cb, cbZero); | ||||||
|  |  | ||||||
|  |                 Marshal.Copy(pbZero, 0, pb, (int)cbBlock); | ||||||
|  |  | ||||||
|  |                 pb = AddPtr(pb, cbBlock); | ||||||
|  |                 cb -= cbBlock; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Convert 2 bytes to a 16-bit unsigned integer (little-endian). |         /// Convert 2 bytes to a 16-bit unsigned integer (little-endian). | ||||||
|         /// </summary> |         /// </summary> | ||||||
| @@ -396,15 +371,7 @@ namespace KeePassLib.Utility | |||||||
|         /// </summary> |         /// </summary> | ||||||
|         public static byte[] UInt16ToBytes(ushort uValue) |         public static byte[] UInt16ToBytes(ushort uValue) | ||||||
|         { |         { | ||||||
| 			byte[] pb = new byte[2]; |             return new byte[2] { (byte)uValue, (byte)(uValue >> 8) }; | ||||||
|  |  | ||||||
| 			unchecked |  | ||||||
| 			{ |  | ||||||
| 				pb[0] = (byte)uValue; |  | ||||||
| 				pb[1] = (byte)(uValue >> 8); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return pb; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @@ -412,17 +379,8 @@ namespace KeePassLib.Utility | |||||||
|         /// </summary> |         /// </summary> | ||||||
|         public static byte[] UInt32ToBytes(uint uValue) |         public static byte[] UInt32ToBytes(uint uValue) | ||||||
|         { |         { | ||||||
| 			byte[] pb = new byte[4]; |             return new byte[4] { (byte)uValue, (byte)(uValue >> 8), | ||||||
|  |                 (byte)(uValue >> 16), (byte)(uValue >> 24) }; | ||||||
| 			unchecked |  | ||||||
| 			{ |  | ||||||
| 				pb[0] = (byte)uValue; |  | ||||||
| 				pb[1] = (byte)(uValue >> 8); |  | ||||||
| 				pb[2] = (byte)(uValue >> 16); |  | ||||||
| 				pb[3] = (byte)(uValue >> 24); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return pb; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @@ -431,41 +389,27 @@ namespace KeePassLib.Utility | |||||||
|         public static void UInt32ToBytesEx(uint uValue, byte[] pb, int iOffset) |         public static void UInt32ToBytesEx(uint uValue, byte[] pb, int iOffset) | ||||||
|         { |         { | ||||||
|             if (pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } |             if (pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } | ||||||
| 			if((iOffset < 0) || ((iOffset + 3) >= pb.Length)) |             if ((iOffset < 0) || (iOffset >= (pb.Length - 3))) | ||||||
|             { |             { | ||||||
|                 Debug.Assert(false); |                 Debug.Assert(false); | ||||||
|                 throw new ArgumentOutOfRangeException("iOffset"); |                 throw new ArgumentOutOfRangeException("iOffset"); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| 			unchecked |  | ||||||
| 			{ |  | ||||||
|             pb[iOffset] = (byte)uValue; |             pb[iOffset] = (byte)uValue; | ||||||
|             pb[iOffset + 1] = (byte)(uValue >> 8); |             pb[iOffset + 1] = (byte)(uValue >> 8); | ||||||
|             pb[iOffset + 2] = (byte)(uValue >> 16); |             pb[iOffset + 2] = (byte)(uValue >> 16); | ||||||
|             pb[iOffset + 3] = (byte)(uValue >> 24); |             pb[iOffset + 3] = (byte)(uValue >> 24); | ||||||
|         } |         } | ||||||
| 		} |  | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Convert a 64-bit unsigned integer to 8 bytes (little-endian). |         /// Convert a 64-bit unsigned integer to 8 bytes (little-endian). | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public static byte[] UInt64ToBytes(ulong uValue) |         public static byte[] UInt64ToBytes(ulong uValue) | ||||||
|         { |         { | ||||||
| 			byte[] pb = new byte[8]; |             return new byte[8] { (byte)uValue, (byte)(uValue >> 8), | ||||||
|  |                 (byte)(uValue >> 16), (byte)(uValue >> 24), | ||||||
| 			unchecked |                 (byte)(uValue >> 32), (byte)(uValue >> 40), | ||||||
| 			{ |                 (byte)(uValue >> 48), (byte)(uValue >> 56) }; | ||||||
| 				pb[0] = (byte)uValue; |  | ||||||
| 				pb[1] = (byte)(uValue >> 8); |  | ||||||
| 				pb[2] = (byte)(uValue >> 16); |  | ||||||
| 				pb[3] = (byte)(uValue >> 24); |  | ||||||
| 				pb[4] = (byte)(uValue >> 32); |  | ||||||
| 				pb[5] = (byte)(uValue >> 40); |  | ||||||
| 				pb[6] = (byte)(uValue >> 48); |  | ||||||
| 				pb[7] = (byte)(uValue >> 56); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return pb; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @@ -474,14 +418,12 @@ namespace KeePassLib.Utility | |||||||
|         public static void UInt64ToBytesEx(ulong uValue, byte[] pb, int iOffset) |         public static void UInt64ToBytesEx(ulong uValue, byte[] pb, int iOffset) | ||||||
|         { |         { | ||||||
|             if (pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } |             if (pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } | ||||||
| 			if((iOffset < 0) || ((iOffset + 7) >= pb.Length)) |             if ((iOffset < 0) || (iOffset >= (pb.Length - 7))) | ||||||
|             { |             { | ||||||
|                 Debug.Assert(false); |                 Debug.Assert(false); | ||||||
|                 throw new ArgumentOutOfRangeException("iOffset"); |                 throw new ArgumentOutOfRangeException("iOffset"); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| 			unchecked |  | ||||||
| 			{ |  | ||||||
|             pb[iOffset] = (byte)uValue; |             pb[iOffset] = (byte)uValue; | ||||||
|             pb[iOffset + 1] = (byte)(uValue >> 8); |             pb[iOffset + 1] = (byte)(uValue >> 8); | ||||||
|             pb[iOffset + 2] = (byte)(uValue >> 16); |             pb[iOffset + 2] = (byte)(uValue >> 16); | ||||||
| @@ -491,18 +433,27 @@ namespace KeePassLib.Utility | |||||||
|             pb[iOffset + 6] = (byte)(uValue >> 48); |             pb[iOffset + 6] = (byte)(uValue >> 48); | ||||||
|             pb[iOffset + 7] = (byte)(uValue >> 56); |             pb[iOffset + 7] = (byte)(uValue >> 56); | ||||||
|         } |         } | ||||||
| 		} |  | ||||||
|  |  | ||||||
|         public static byte[] Int32ToBytes(int iValue) |         public static byte[] Int32ToBytes(int iValue) | ||||||
|         { |         { | ||||||
|             return UInt32ToBytes((uint)iValue); |             return UInt32ToBytes((uint)iValue); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public static void Int32ToBytesEx(int iValue, byte[] pb, int iOffset) | ||||||
|  |         { | ||||||
|  |             UInt32ToBytesEx((uint)iValue, pb, iOffset); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public static byte[] Int64ToBytes(long lValue) |         public static byte[] Int64ToBytes(long lValue) | ||||||
|         { |         { | ||||||
|             return UInt64ToBytes((ulong)lValue); |             return UInt64ToBytes((ulong)lValue); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public static void Int64ToBytesEx(long lValue, byte[] pb, int iOffset) | ||||||
|  |         { | ||||||
|  |             UInt64ToBytesEx((ulong)lValue, pb, iOffset); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public static uint RotateLeft32(uint u, int nBits) |         public static uint RotateLeft32(uint u, int nBits) | ||||||
|         { |         { | ||||||
|             return ((u << nBits) | (u >> (32 - nBits))); |             return ((u << nBits) | (u >> (32 - nBits))); | ||||||
| @@ -523,14 +474,35 @@ namespace KeePassLib.Utility | |||||||
|             return ((u >> nBits) | (u << (64 - nBits))); |             return ((u >> nBits) | (u << (64 - nBits))); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         private static void AddVersionComponent(ref ulong uVersion, int iValue) | ||||||
|  |         { | ||||||
|  |             if (iValue < 0) iValue = 0; | ||||||
|  |             else if (iValue > 0xFFFF) { Debug.Assert(false); iValue = 0xFFFF; } | ||||||
|  |  | ||||||
|  |             uVersion = (uVersion << 16) | (uint)iValue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         internal static ulong VersionToUInt64(Version v) | ||||||
|  |         { | ||||||
|  |             if (v == null) { Debug.Assert(false); return 0; } | ||||||
|  |  | ||||||
|  |             ulong u = 0; | ||||||
|  |             AddVersionComponent(ref u, v.Major); | ||||||
|  |             AddVersionComponent(ref u, v.Minor); | ||||||
|  |             AddVersionComponent(ref u, v.Build); | ||||||
|  |             AddVersionComponent(ref u, v.Revision); | ||||||
|  |             return u; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public static bool ArraysEqual(byte[] x, byte[] y) |         public static bool ArraysEqual(byte[] x, byte[] y) | ||||||
|         { |         { | ||||||
|             // Return false if one of them is null (not comparable)! |             // Return false if one of them is null (not comparable)! | ||||||
|             if ((x == null) || (y == null)) { Debug.Assert(false); return false; } |             if ((x == null) || (y == null)) { Debug.Assert(false); return false; } | ||||||
|  |  | ||||||
| 			if(x.Length != y.Length) return false; |             int cb = x.Length; | ||||||
|  |             if (cb != y.Length) return false; | ||||||
|  |  | ||||||
| 			for(int i = 0; i < x.Length; ++i) |             for (int i = 0; i < cb; ++i) | ||||||
|             { |             { | ||||||
|                 if (x[i] != y[i]) return false; |                 if (x[i] != y[i]) return false; | ||||||
|             } |             } | ||||||
| @@ -556,28 +528,90 @@ namespace KeePassLib.Utility | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
| 		/// Fast hash that can be used e.g. for hash tables. |         /// Fast 32-bit hash (e.g. for hash tables). | ||||||
|         /// The algorithm might change in the future; do not store |         /// The algorithm might change in the future; do not store | ||||||
|         /// the hashes for later use. |         /// the hashes for later use. | ||||||
|         /// </summary> |         /// </summary> | ||||||
| 		public static uint Hash32(byte[] v, int iStart, int iLength) |         public static uint Hash32(byte[] pb, int iOffset, int cb) | ||||||
|         { |         { | ||||||
| 			uint u = 0x326F637B; |             const ulong hI = 0x4295DC458269ED9DUL; | ||||||
|  |             const uint hI32 = (uint)(hI >> 32); | ||||||
|  |  | ||||||
| 			if(v == null) { Debug.Assert(false); return u; } |             if (pb == null) { Debug.Assert(false); return hI32; } | ||||||
| 			if(iStart < 0) { Debug.Assert(false); return u; } |             if (iOffset < 0) { Debug.Assert(false); return hI32; } | ||||||
| 			if(iLength < 0) { Debug.Assert(false); return u; } |             if (cb < 0) { Debug.Assert(false); return hI32; } | ||||||
|  |  | ||||||
| 			int m = iStart + iLength; |             int m = iOffset + cb; | ||||||
| 			if(m > v.Length) { Debug.Assert(false); return u; } |             if ((m < 0) || (m > pb.Length)) { Debug.Assert(false); return hI32; } | ||||||
|  |  | ||||||
| 			for(int i = iStart; i < m; ++i) |             int m4 = iOffset + (cb & ~3), cbR = cb & 3; | ||||||
|  |             ulong h = hI; | ||||||
|  |  | ||||||
|  |             for (int i = iOffset; i < m4; i += 4) | ||||||
|  |                 h = (pb[i] ^ ((ulong)pb[i + 1] << 8) ^ ((ulong)pb[i + 2] << 16) ^ | ||||||
|  |                     ((ulong)pb[i + 3] << 24) ^ h) * 0x5EA4A1E35C8ACDA3UL; | ||||||
|  |  | ||||||
|  |             switch (cbR) | ||||||
|             { |             { | ||||||
| 				u ^= m_vSBox[v[i]]; |                 case 1: | ||||||
| 				u *= 3; |                     Debug.Assert(m4 == (m - 1)); | ||||||
|  |                     h = (pb[m4] ^ h) * 0x54A1CC5970AF27BBUL; | ||||||
|  |                     break; | ||||||
|  |                 case 2: | ||||||
|  |                     Debug.Assert(m4 == (m - 2)); | ||||||
|  |                     h = (pb[m4] ^ ((ulong)pb[m4 + 1] << 8) ^ h) * | ||||||
|  |                         0x6C45CB2537A4271DUL; | ||||||
|  |                     break; | ||||||
|  |                 case 3: | ||||||
|  |                     Debug.Assert(m4 == (m - 3)); | ||||||
|  |                     h = (pb[m4] ^ ((ulong)pb[m4 + 1] << 8) ^ | ||||||
|  |                         ((ulong)pb[m4 + 2] << 16) ^ h) * 0x59B8E8939E19695DUL; | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     Debug.Assert(m4 == m); | ||||||
|  |                     break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| 			return u; |             Debug.Assert((cb != 0) || ((uint)(h >> 32) == hI32)); | ||||||
|  |             return (uint)(h >> 32); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         internal static uint Hash32Ex<T>(T[] v, int iOffset, int c) | ||||||
|  |         { | ||||||
|  |             const ulong hI = 0x4295DC458269ED9DUL; | ||||||
|  |             const uint hI32 = (uint)(hI >> 32); | ||||||
|  |  | ||||||
|  |             if (v == null) { Debug.Assert(false); return hI32; } | ||||||
|  |             if (iOffset < 0) { Debug.Assert(false); return hI32; } | ||||||
|  |             if (c < 0) { Debug.Assert(false); return hI32; } | ||||||
|  |  | ||||||
|  |             int m = iOffset + c; | ||||||
|  |             if ((m < 0) || (m > v.Length)) { Debug.Assert(false); return hI32; } | ||||||
|  |  | ||||||
|  |             ulong h = hI; | ||||||
|  |  | ||||||
|  |             for (int i = iOffset; i < m; ++i) | ||||||
|  |                 h = (h ^ (uint)v[i].GetHashCode()) * 0x5EA4A1E35C8ACDA3UL; | ||||||
|  |  | ||||||
|  |             Debug.Assert((c != 0) || ((uint)(h >> 32) == hI32)); | ||||||
|  |             return (uint)(h >> 32); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         internal static ulong Hash64(int[] v, int iOffset, int ci) | ||||||
|  |         { | ||||||
|  |             ulong h = 0x4295DC458269ED9DUL; | ||||||
|  |  | ||||||
|  |             if (v == null) { Debug.Assert(false); return h; } | ||||||
|  |             if (iOffset < 0) { Debug.Assert(false); return h; } | ||||||
|  |             if (ci < 0) { Debug.Assert(false); return h; } | ||||||
|  |  | ||||||
|  |             int m = iOffset + ci; | ||||||
|  |             if ((m < 0) || (m > v.Length)) { Debug.Assert(false); return h; } | ||||||
|  |  | ||||||
|  |             for (int i = iOffset; i < m; ++i) | ||||||
|  |                 h = (h ^ (uint)v[i]) * 0x5EA4A1E35C8ACDA3UL; | ||||||
|  |  | ||||||
|  |             return ((h ^ (h >> 32)) * 0x59B8E8939E19695DUL); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public static void CopyStream(Stream sSource, Stream sTarget) |         public static void CopyStream(Stream sSource, Stream sTarget) | ||||||
| @@ -586,20 +620,31 @@ namespace KeePassLib.Utility | |||||||
|             if (sSource == null) throw new ArgumentNullException("sSource"); |             if (sSource == null) throw new ArgumentNullException("sSource"); | ||||||
|             if (sTarget == null) throw new ArgumentNullException("sTarget"); |             if (sTarget == null) throw new ArgumentNullException("sTarget"); | ||||||
|  |  | ||||||
| 			const int nBufSize = 4096; |             const int cbBuf = 4096; | ||||||
| 			byte[] pbBuf = new byte[nBufSize]; |             byte[] pbBuf = new byte[cbBuf]; | ||||||
|  |  | ||||||
|             while (true) |             while (true) | ||||||
|             { |             { | ||||||
| 				int nRead = sSource.Read(pbBuf, 0, nBufSize); |                 int cbRead = sSource.Read(pbBuf, 0, cbBuf); | ||||||
| 				if(nRead == 0) break; |                 if (cbRead == 0) break; | ||||||
|  |  | ||||||
| 				sTarget.Write(pbBuf, 0, nRead); |                 sTarget.Write(pbBuf, 0, cbRead); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             // Do not close any of the streams |             // Do not close any of the streams | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public static byte[] Read(Stream s) | ||||||
|  |         { | ||||||
|  |             if (s == null) throw new ArgumentNullException("s"); | ||||||
|  |  | ||||||
|  |             using (MemoryStream ms = new MemoryStream()) | ||||||
|  |             { | ||||||
|  |                 CopyStream(s, ms); | ||||||
|  |                 return ms.ToArray(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public static byte[] Read(Stream s, int nCount) |         public static byte[] Read(Stream s, int nCount) | ||||||
|         { |         { | ||||||
|             if (s == null) throw new ArgumentNullException("s"); |             if (s == null) throw new ArgumentNullException("s"); | ||||||
| @@ -626,6 +671,17 @@ namespace KeePassLib.Utility | |||||||
|             return pb; |             return pb; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         internal static string ReadString(Stream s, Encoding enc) | ||||||
|  |         { | ||||||
|  |             if (s == null) throw new ArgumentNullException("s"); | ||||||
|  |             if (enc == null) throw new ArgumentNullException("enc"); | ||||||
|  |  | ||||||
|  |             using (StreamReader sr = new StreamReader(s, enc, true)) | ||||||
|  |             { | ||||||
|  |                 return sr.ReadToEnd(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public static void Write(Stream s, byte[] pbData) |         public static void Write(Stream s, byte[] pbData) | ||||||
|         { |         { | ||||||
|             if (s == null) { Debug.Assert(false); return; } |             if (s == null) { Debug.Assert(false); return; } | ||||||
| @@ -640,7 +696,6 @@ namespace KeePassLib.Utility | |||||||
|             if (pbData == null) throw new ArgumentNullException("pbData"); |             if (pbData == null) throw new ArgumentNullException("pbData"); | ||||||
|             if (pbData.Length == 0) return pbData; |             if (pbData.Length == 0) return pbData; | ||||||
|  |  | ||||||
| 			byte[] pbCompressed; |  | ||||||
|             using (MemoryStream msSource = new MemoryStream(pbData, false)) |             using (MemoryStream msSource = new MemoryStream(pbData, false)) | ||||||
|             { |             { | ||||||
|                 using (MemoryStream msCompressed = new MemoryStream()) |                 using (MemoryStream msCompressed = new MemoryStream()) | ||||||
| @@ -648,14 +703,12 @@ namespace KeePassLib.Utility | |||||||
|                     using (GZipStream gz = new GZipStream(msCompressed, |                     using (GZipStream gz = new GZipStream(msCompressed, | ||||||
|                         CompressionMode.Compress)) |                         CompressionMode.Compress)) | ||||||
|                     { |                     { | ||||||
| 						MemUtil.CopyStream(msSource, gz); |                         CopyStream(msSource, gz); | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
| 					pbCompressed = msCompressed.ToArray(); |                     return msCompressed.ToArray(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| 			return pbCompressed; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public static byte[] Decompress(byte[] pbCompressed) |         public static byte[] Decompress(byte[] pbCompressed) | ||||||
| @@ -663,7 +716,6 @@ namespace KeePassLib.Utility | |||||||
|             if (pbCompressed == null) throw new ArgumentNullException("pbCompressed"); |             if (pbCompressed == null) throw new ArgumentNullException("pbCompressed"); | ||||||
|             if (pbCompressed.Length == 0) return pbCompressed; |             if (pbCompressed.Length == 0) return pbCompressed; | ||||||
|  |  | ||||||
| 			byte[] pbData; |  | ||||||
|             using (MemoryStream msData = new MemoryStream()) |             using (MemoryStream msData = new MemoryStream()) | ||||||
|             { |             { | ||||||
|                 using (MemoryStream msCompressed = new MemoryStream(pbCompressed, false)) |                 using (MemoryStream msCompressed = new MemoryStream(pbCompressed, false)) | ||||||
| @@ -671,14 +723,12 @@ namespace KeePassLib.Utility | |||||||
|                     using (GZipStream gz = new GZipStream(msCompressed, |                     using (GZipStream gz = new GZipStream(msCompressed, | ||||||
|                         CompressionMode.Decompress)) |                         CompressionMode.Decompress)) | ||||||
|                     { |                     { | ||||||
| 						MemUtil.CopyStream(gz, msData); |                         CopyStream(gz, msData); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| 				pbData = msData.ToArray(); |                 return msData.ToArray(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| 			return pbData; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public static int IndexOf<T>(T[] vHaystack, T[] vNeedle) |         public static int IndexOf<T>(T[] vHaystack, T[] vNeedle) | ||||||
| @@ -688,10 +738,13 @@ namespace KeePassLib.Utility | |||||||
|             if (vNeedle == null) throw new ArgumentNullException("vNeedle"); |             if (vNeedle == null) throw new ArgumentNullException("vNeedle"); | ||||||
|             if (vNeedle.Length == 0) return 0; |             if (vNeedle.Length == 0) return 0; | ||||||
|  |  | ||||||
| 			for(int i = 0; i <= (vHaystack.Length - vNeedle.Length); ++i) |             int cN = vNeedle.Length; | ||||||
|  |             int iMax = vHaystack.Length - cN; | ||||||
|  |  | ||||||
|  |             for (int i = 0; i <= iMax; ++i) | ||||||
|             { |             { | ||||||
|                 bool bFound = true; |                 bool bFound = true; | ||||||
| 				for(int m = 0; m < vNeedle.Length; ++m) |                 for (int m = 0; m < cN; ++m) | ||||||
|                 { |                 { | ||||||
|                     if (!vHaystack[i + m].Equals(vNeedle[m])) |                     if (!vHaystack[i + m].Equals(vNeedle[m])) | ||||||
|                     { |                     { | ||||||
| @@ -786,6 +839,44 @@ namespace KeePassLib.Utility | |||||||
|  |  | ||||||
|             yield break; |             yield break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         internal static IEnumerable<T> Distinct<T, TKey>(IEnumerable<T> s, | ||||||
|  |             GFunc<T, TKey> fGetKey, bool bPreferLast) | ||||||
|  |         { | ||||||
|  |             if (s == null) throw new ArgumentNullException("s"); | ||||||
|  |             if (fGetKey == null) throw new ArgumentNullException("fGetKey"); | ||||||
|  |  | ||||||
|  |             Dictionary<TKey, bool> d = new Dictionary<TKey, bool>(); | ||||||
|  |  | ||||||
|  |             if (bPreferLast) | ||||||
|  |             { | ||||||
|  |                 List<T> l = new List<T>(s); | ||||||
|  |                 int n = l.Count; | ||||||
|  |                 bool[] v = new bool[n]; | ||||||
|  |  | ||||||
|  |                 for (int i = n - 1; i >= 0; --i) | ||||||
|  |                 { | ||||||
|  |                     TKey k = fGetKey(l[i]); | ||||||
|  |                     if (!d.ContainsKey(k)) { d[k] = true; v[i] = true; } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 for (int i = 0; i < n; ++i) | ||||||
|  |                 { | ||||||
|  |                     if (v[i]) yield return l[i]; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 foreach (T t in s) | ||||||
|  |                 { | ||||||
|  |                     TKey k = fGetKey(t); | ||||||
|  |                     if (!d.ContainsKey(k)) { d[k] = true; yield return t; } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             yield break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         internal static bool ListsEqual<T>(List<T> a, List<T> b) |         internal static bool ListsEqual<T>(List<T> a, List<T> b) | ||||||
|             where T : class, IEquatable<T> |             where T : class, IEquatable<T> | ||||||
|         { |         { | ||||||
| @@ -809,5 +900,164 @@ namespace KeePassLib.Utility | |||||||
|  |  | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         internal static int Count(byte[] pb, byte bt) | ||||||
|  |         { | ||||||
|  |             if (pb == null) { Debug.Assert(false); return 0; } | ||||||
|  |  | ||||||
|  |             int cb = pb.Length, r = 0; | ||||||
|  |             for (int i = 0; i < cb; ++i) | ||||||
|  |             { | ||||||
|  |                 if (pb[i] == bt) ++r; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return r; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [MethodImpl(MioNoOptimize)] | ||||||
|  |         internal static void DisposeIfPossible(object o) | ||||||
|  |         { | ||||||
|  |             if (o == null) { Debug.Assert(false); return; } | ||||||
|  |  | ||||||
|  |             IDisposable d = (o as IDisposable); | ||||||
|  |             if (d != null) d.Dispose(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         internal static object GetEnumValue(Type tEnum, string strName) | ||||||
|  |         { | ||||||
|  |             if (tEnum == null) { Debug.Assert(false); return null; } | ||||||
|  |             if (!tEnum.IsEnum) { Debug.Assert(false); return null; } | ||||||
|  |             if (string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; } | ||||||
|  |  | ||||||
|  |             return ((Array.IndexOf<string>(Enum.GetNames(tEnum), strName) >= 0) ? | ||||||
|  |                 Enum.Parse(tEnum, strName) : null); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         internal static T ConvertObject<T>(object o, T tDefault) | ||||||
|  |         { | ||||||
|  |             if (o == null) return tDefault; | ||||||
|  |  | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 if (o is T) return (T)o; | ||||||
|  |                 return (T)Convert.ChangeType(o, typeof(T)); | ||||||
|  |             } | ||||||
|  |             catch (Exception) { Debug.Assert(false); } | ||||||
|  |  | ||||||
|  |             try { return (T)o; } | ||||||
|  |             catch (Exception) { Debug.Assert(false); } | ||||||
|  |  | ||||||
|  |             return tDefault; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         internal static T BytesToStruct<T>(byte[] pb, int iOffset) | ||||||
|  |             where T : struct | ||||||
|  |         { | ||||||
|  |             if (pb == null) throw new ArgumentNullException("pb"); | ||||||
|  |             if (iOffset < 0) throw new ArgumentOutOfRangeException("iOffset"); | ||||||
|  |  | ||||||
|  |             int cb = Marshal.SizeOf(typeof(T)); | ||||||
|  |             if (cb <= 0) { Debug.Assert(false); return default(T); } | ||||||
|  |  | ||||||
|  |             if (iOffset > (pb.Length - cb)) throw new ArgumentOutOfRangeException("iOffset"); | ||||||
|  |  | ||||||
|  |             IntPtr p = Marshal.AllocCoTaskMem(cb); | ||||||
|  |             if (p == IntPtr.Zero) throw new OutOfMemoryException(); | ||||||
|  |  | ||||||
|  |             object o; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 Marshal.Copy(pb, iOffset, p, cb); | ||||||
|  |                 o = Marshal.PtrToStructure(p, typeof(T)); | ||||||
|  |             } | ||||||
|  |             finally { Marshal.FreeCoTaskMem(p); } | ||||||
|  |  | ||||||
|  |             return (T)o; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         internal static byte[] StructToBytes<T>(ref T t) | ||||||
|  |             where T : struct | ||||||
|  |         { | ||||||
|  |             int cb = Marshal.SizeOf(typeof(T)); | ||||||
|  |             if (cb <= 0) { Debug.Assert(false); return MemUtil.EmptyByteArray; } | ||||||
|  |  | ||||||
|  |             byte[] pb = new byte[cb]; | ||||||
|  |  | ||||||
|  |             IntPtr p = Marshal.AllocCoTaskMem(cb); | ||||||
|  |             if (p == IntPtr.Zero) throw new OutOfMemoryException(); | ||||||
|  |  | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 Marshal.StructureToPtr(t, p, false); | ||||||
|  |                 Marshal.Copy(p, pb, 0, cb); | ||||||
|  |             } | ||||||
|  |             finally { Marshal.FreeCoTaskMem(p); } | ||||||
|  |  | ||||||
|  |             return pb; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         internal static IntPtr AddPtr(IntPtr p, long cb) | ||||||
|  |         { | ||||||
|  |             // IntPtr.operator+ and IntPtr.Add are not available in .NET 2.0 | ||||||
|  |  | ||||||
|  |             if (IntPtr.Size >= 8) | ||||||
|  |                 return new IntPtr(unchecked(p.ToInt64() + cb)); | ||||||
|  |             return new IntPtr(unchecked(p.ToInt32() + (int)cb)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Cf. Array.Empty<T>() of .NET 4.6 | ||||||
|  |         private static class EmptyArrayEx<T> | ||||||
|  |         { | ||||||
|  |             internal static readonly T[] Instance = new T[0]; | ||||||
|  |         } | ||||||
|  |         internal static T[] EmptyArray<T>() | ||||||
|  |         { | ||||||
|  |             return EmptyArrayEx<T>.Instance; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal sealed class ArrayHelperEx<T> : IEqualityComparer<T[]>, IComparer<T[]> | ||||||
|  |         where T : IEquatable<T>, IComparable<T> | ||||||
|  |     { | ||||||
|  |         public int GetHashCode(T[] obj) | ||||||
|  |         { | ||||||
|  |             if (obj == null) { Debug.Assert(false); throw new ArgumentNullException("obj"); } | ||||||
|  |  | ||||||
|  |             return (int)MemUtil.Hash32Ex<T>(obj, 0, obj.Length); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public bool Equals(T[] x, T[] y) | ||||||
|  |         { | ||||||
|  |             if (object.ReferenceEquals(x, y)) return true; | ||||||
|  |             if ((x == null) || (y == null)) return false; | ||||||
|  |  | ||||||
|  |             int n = x.Length; | ||||||
|  |             if (n != y.Length) return false; | ||||||
|  |  | ||||||
|  |             for (int i = 0; i < n; ++i) | ||||||
|  |             { | ||||||
|  |                 if (!x[i].Equals(y[i])) return false; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public int Compare(T[] x, T[] y) | ||||||
|  |         { | ||||||
|  |             if (object.ReferenceEquals(x, y)) return 0; | ||||||
|  |             if (x == null) return -1; | ||||||
|  |             if (y == null) return 1; | ||||||
|  |  | ||||||
|  |             int n = x.Length, m = y.Length; | ||||||
|  |             if (n != m) return ((n < m) ? -1 : 1); | ||||||
|  |  | ||||||
|  |             for (int i = 0; i < n; ++i) | ||||||
|  |             { | ||||||
|  |                 T tX = x[i], tY = y[i]; | ||||||
|  |                 if (!tX.Equals(tY)) return tX.CompareTo(tY); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -58,7 +58,8 @@ | |||||||
|       "IsFocused": false, |       "IsFocused": false, | ||||||
|       "InputType": 97, |       "InputType": 97, | ||||||
|       "HtmlInfoTag": null, |       "HtmlInfoTag": null, | ||||||
|       "HtmlInfoTypeAttribute": null |       "HtmlInfoTypeAttribute": null, | ||||||
|  |       "ExpectedAssignedHints": [ "username" ] | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "IdEntry": "password_text_input_layout", |       "IdEntry": "password_text_input_layout", | ||||||
| @@ -81,6 +82,7 @@ | |||||||
|       "InputType": 129, |       "InputType": 129, | ||||||
|       "HtmlInfoTag": null, |       "HtmlInfoTag": null, | ||||||
|       "HtmlInfoTypeAttribute": null, |       "HtmlInfoTypeAttribute": null, | ||||||
|  |       "ExpectedAssignedHints": [ "password" ] | ||||||
|  |  | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -801,7 +801,7 @@ namespace Kp2aAutofillParser | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public AutofillTargetId ParseForFill(bool isManual, AutofillView<FieldT> autofillView) |         public AutofillTargetId ParseForFill(AutofillView<FieldT> autofillView) | ||||||
|         { |         { | ||||||
|             return Parse(true, autofillView); |             return Parse(true, autofillView); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,13 +0,0 @@ | |||||||
| namespace keepass2android.Io |  | ||||||
| { |  | ||||||
| 	public partial class DropboxFileStorage |  | ||||||
| 	{ |  | ||||||
| 		private const string AppKey = "dummy"; |  | ||||||
| 		private const string AppSecret = "dummy"; |  | ||||||
| 	} |  | ||||||
| 	public partial class DropboxAppFolderFileStorage |  | ||||||
| 	{ |  | ||||||
| 		private const string AppKey = "dummy"; |  | ||||||
| 		private const string AppSecret = "dummy"; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										27
									
								
								src/Kp2aBusinessLogic/Io/GenerateSecrets.targets
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/Kp2aBusinessLogic/Io/GenerateSecrets.targets
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | <Project> | ||||||
|  |   <Target Name="GenerateDropboxSecrets" BeforeTargets="BeforeCompile" | ||||||
|  |   Inputs="@(DropboxSecretLines)" | ||||||
|  |   Outputs="DropboxFileStorage.g.cs"> | ||||||
|  |  | ||||||
|  |     <WriteLinesToFile | ||||||
|  |     File="Io/DropboxFileStorage.g.cs" | ||||||
|  |       Lines="@(DropboxSecretLines->'%(Text)')" | ||||||
|  |       Overwrite="true" | ||||||
|  |       /> | ||||||
|  |   </Target> | ||||||
|  |  | ||||||
|  |   <ItemGroup> | ||||||
|  |     <DropboxSecretLines Include="GeneratedDropboxSecrets"> | ||||||
|  |       <Text>namespace keepass2android.Io { | ||||||
|  |     public partial class DropboxFileStorage { | ||||||
|  |       private const string AppKey = "$(DropboxAppKey)"; | ||||||
|  |       private const string AppSecret = "$(DropboxAppSecret)"; | ||||||
|  |     } | ||||||
|  |     public partial class DropboxAppFolderFileStorage { | ||||||
|  |       private const string AppKey = "$(DropboxAppFolderAppKey)"; | ||||||
|  |       private const string AppSecret = "$(DropboxAppFolderAppSecret)"; | ||||||
|  |     } | ||||||
|  |   }</Text> | ||||||
|  |     </DropboxSecretLines> | ||||||
|  |   </ItemGroup> | ||||||
|  | </Project> | ||||||
| @@ -4,30 +4,43 @@ | |||||||
|     <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion> |     <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion> | ||||||
|     <Nullable>enable</Nullable> |     <Nullable>enable</Nullable> | ||||||
|     <ImplicitUsings>enable</ImplicitUsings> |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|  |     <DefineConstants Condition="'$(Flavor)'=='NoNet'">NO_QR_SCANNER;EXCLUDE_JAVAFILESTORAGE;NoNet</DefineConstants> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Folder Include="Resources\" /> |     <Folder Include="Resources\" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <PackageReference Include="FluentFTP" Version="51.1.0" /> |     <PackageReference Include="FluentFTP" Version="51.1.0" Condition="'$(Flavor)'!='NoNet'"/> | ||||||
|     <PackageReference Include="MegaApiClient" Version="1.10.4" /> |     <PackageReference Include="MegaApiClient" Version="1.10.4"  Condition="'$(Flavor)'!='NoNet'"/> | ||||||
|     <PackageReference Include="Microsoft.Graph" Version="5.68.0" /> |     <PackageReference Include="Microsoft.Graph" Version="5.68.0" Condition="'$(Flavor)'!='NoNet'"/> | ||||||
|     <PackageReference Include="Microsoft.Identity.Client" Version="4.67.1" /> |     <PackageReference Include="Microsoft.Identity.Client" Version="4.67.1" Condition="'$(Flavor)'!='NoNet'"/> | ||||||
|     <PackageReference Include="Xamarin.AndroidX.Browser" Version="1.8.0" /> |     <PackageReference Include="Xamarin.AndroidX.Browser" Version="1.8.0" /> | ||||||
|     <PackageReference Include="Xamarin.AndroidX.Core" Version="1.13.1.5" /> |     <PackageReference Include="Xamarin.AndroidX.Core" Version="1.13.1.5" /> | ||||||
|     <PackageReference Include="Xamarin.Google.Android.Material" Version="1.11.0.3" /> |     <PackageReference Include="Xamarin.Google.Android.Material" Version="1.11.0.3" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj" /> |     <ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj" /> | ||||||
|     <ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj" /> |     <ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj" Condition="'$(Flavor)'!='NoNet'"  /> | ||||||
|     <ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj" /> |     <ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj" /> | ||||||
|     <ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj" /> |     <ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj" /> | ||||||
|     <ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj" /> |     <ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|   <None Remove="Io/DropboxFileStorageKeysDummy.cs" /> |  | ||||||
|   <Compile Remove="Io/DropboxFileStorageKeysDummy.cs" /> | </ItemGroup> | ||||||
|   <Content Remove="Io/DropboxFileStorageKeysDummy.cs" /> | <ItemGroup  Condition="'$(Flavor)'=='NoNet'"> | ||||||
|  |   <None Remove="Io/OneDrive2FileStorage.cs" /> | ||||||
|  |   <Compile Remove="Io/OneDrive2FileStorage.cs" /> | ||||||
|  |   <Content Remove="Io/OneDrive2FileStorage.cs" /> | ||||||
|  |   <None Remove="Io/MegaFileStorage.cs" /> | ||||||
|  |   <Compile Remove="Io/MegaFileStorage.cs" /> | ||||||
|  |   <Content Remove="Io/MegaFileStorage.cs" /> | ||||||
| </ItemGroup> | </ItemGroup> | ||||||
|  |  | ||||||
|  | <Import Project="Io/GenerateSecrets.targets" /> | ||||||
|  | <ItemGroup> | ||||||
|  | <Compile Include="Io/DropboxFileStorage.g.cs" /> | ||||||
|  | </ItemGroup> | ||||||
|  |  | ||||||
|  |    | ||||||
| </Project> | </Project> | ||||||
| @@ -20,7 +20,6 @@ git clone --recurse-submodules https://github.com/PhilippC/keepass2android.git | |||||||
| cd keepass2android/src/build-scripts | cd keepass2android/src/build-scripts | ||||||
| ./build-java.sh && ./build-native.sh | ./build-java.sh && ./build-native.sh | ||||||
| cd .. | cd .. | ||||||
| cp Kp2aBusinessLogic/Io/DropboxFileStorageKeysDummy.cs Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs |  | ||||||
| cd keepass2android-app | cd keepass2android-app | ||||||
| ln -s Manifests/AndroidManifest_debug.xml AndroidManifest.xml | ln -s Manifests/AndroidManifest_debug.xml AndroidManifest.xml | ||||||
| dotnet workload restore | dotnet workload restore | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								src/build-scripts/rename-output-apks.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/build-scripts/rename-output-apks.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  |  | ||||||
|  | BASE_DIR="${1}" | ||||||
|  |  | ||||||
|  | for arch_dir in "$BASE_DIR"/android-*/; do | ||||||
|  |   arch=$(basename "$arch_dir") | ||||||
|  |   arch=${arch#android-} | ||||||
|  |   APK_DIR="${arch_dir}publish" | ||||||
|  |   if [[ -d "$APK_DIR" ]]; then | ||||||
|  |     apk_path=$(find "$APK_DIR" -maxdepth 1 -type f -name "*.apk" | head -n1) | ||||||
|  |     if [[ -n "$apk_path" ]]; then | ||||||
|  |       base=$(basename "$apk_path" .apk) | ||||||
|  |       new_path="$APK_DIR/${base}-${arch}.apk" | ||||||
|  |       mv "$apk_path" "$new_path" | ||||||
|  |       echo "Renamed $apk_path to $new_path" | ||||||
|  |     else | ||||||
|  |       echo "No APK found in $APK_DIR" | ||||||
|  |     fi | ||||||
|  |   else | ||||||
|  |     echo "Directory $APK_DIR does not exist" | ||||||
|  |   fi | ||||||
|  | done | ||||||
| @@ -6,8 +6,7 @@ | |||||||
|         android:allowBackup="true" |         android:allowBackup="true" | ||||||
|         android:icon="@mipmap/ic_launcher" |         android:icon="@mipmap/ic_launcher" | ||||||
|         android:label="@string/app_name" |         android:label="@string/app_name" | ||||||
|         android:supportsRtl="true" |         android:supportsRtl="true"> | ||||||
|         android:theme="@style/AppTheme"> |  | ||||||
|         <activity android:name="com.crocoapps.javafilestoragetest2.MainActivity" |         <activity android:name="com.crocoapps.javafilestoragetest2.MainActivity" | ||||||
|                   android:exported="true"> |                   android:exported="true"> | ||||||
|             <intent-filter> |             <intent-filter> | ||||||
|   | |||||||
							
								
								
									
										10183
									
								
								src/keepass2android-app/Assets/MostPopularPasswords.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10183
									
								
								src/keepass2android-app/Assets/MostPopularPasswords.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -19,7 +19,7 @@ using keepass2android.services.AutofillBase; | |||||||
|  |  | ||||||
| namespace keepass2android | namespace keepass2android | ||||||
| { | { | ||||||
|     [Activity(Label = "DisableAutofillForQueryActivity")] |     [Activity(Label = "DisableAutofillForQueryActivity", Theme = "@style/Kp2aTheme_ActionBar")] | ||||||
|     public class DisableAutofillForQueryActivity : Activity |     public class DisableAutofillForQueryActivity : Activity | ||||||
|     { |     { | ||||||
|         public IAutofillIntentBuilder IntentBuilder = new Kp2aAutofillIntentBuilder(); |         public IAutofillIntentBuilder IntentBuilder = new Kp2aAutofillIntentBuilder(); | ||||||
|   | |||||||
| @@ -1546,10 +1546,10 @@ namespace keepass2android | |||||||
|             string url = _stringViews[urlFieldKey].Text; |             string url = _stringViews[urlFieldKey].Text; | ||||||
| 			if (url == null) return false; | 			if (url == null) return false; | ||||||
|  |  | ||||||
| 			// Default http:// if no protocol specified | 			// Default https:// if no protocol specified | ||||||
| 			if ((!url.Contains(":") || (url.StartsWith("www.")))) | 			if ((!url.Contains(":") || (url.StartsWith("www.")))) | ||||||
| 			{ | 			{ | ||||||
| 				url = "http://" + url; | 				url = "https://" + url; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			try | 			try | ||||||
|   | |||||||
| @@ -37,8 +37,10 @@ using System.Net; | |||||||
| using System.Text; | using System.Text; | ||||||
| using Android.Content.Res; | using Android.Content.Res; | ||||||
| using Android.Database; | using Android.Database; | ||||||
|  | #if !NO_QR_SCANNER | ||||||
| using Android.Gms.Common; | using Android.Gms.Common; | ||||||
| using Android.Gms.Tasks; | using Android.Gms.Tasks; | ||||||
|  | #endif | ||||||
| using Android.Graphics; | using Android.Graphics; | ||||||
| using Android.Graphics.Drawables; | using Android.Graphics.Drawables; | ||||||
| using Android.Runtime; | using Android.Runtime; | ||||||
| @@ -55,10 +57,11 @@ using Object = Java.Lang.Object; | |||||||
| using Uri = Android.Net.Uri; | using Uri = Android.Net.Uri; | ||||||
| using Resource = keepass2android.Resource; | using Resource = keepass2android.Resource; | ||||||
| using Google.Android.Material.TextField; | using Google.Android.Material.TextField; | ||||||
|  | #if !NO_QR_SCANNER | ||||||
| using Xamarin.Google.MLKit.Vision.Barcode.Common; | using Xamarin.Google.MLKit.Vision.Barcode.Common; | ||||||
| using Xamarin.Google.MLKit.Vision.CodeScanner; | using Xamarin.Google.MLKit.Vision.CodeScanner; | ||||||
|  | #endif | ||||||
| using Console = System.Console; | using Console = System.Console; | ||||||
| using Task = Android.Gms.Tasks.Task; |  | ||||||
|  |  | ||||||
| namespace keepass2android | namespace keepass2android | ||||||
| { | { | ||||||
| @@ -1158,7 +1161,9 @@ namespace keepass2android | |||||||
|             { |             { | ||||||
|                 dlgView.FindViewById(Resource.Id.totp_custom_settings_group).Visibility = args.IsChecked ? ViewStates.Visible : ViewStates.Gone; |                 dlgView.FindViewById(Resource.Id.totp_custom_settings_group).Visibility = args.IsChecked ? ViewStates.Visible : ViewStates.Gone; | ||||||
|             }; |             }; | ||||||
|  | #if NO_QR_SCANNER | ||||||
|  |             dlgView.FindViewById<Button>(Resource.Id.totp_scan).Visibility = ViewStates.Gone; | ||||||
|  | #else | ||||||
| 			dlgView.FindViewById<Button>(Resource.Id.totp_scan).Click += async (object o, EventArgs args) => | 			dlgView.FindViewById<Button>(Resource.Id.totp_scan).Click += async (object o, EventArgs args) => | ||||||
|             { |             { | ||||||
|                 if (GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this) != ConnectionResult.Success) |                 if (GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this) != ConnectionResult.Success) | ||||||
| @@ -1194,6 +1199,7 @@ namespace keepass2android | |||||||
|  |  | ||||||
|  |  | ||||||
|             }; |             }; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| 			//copy values from entry into dialog | 			//copy values from entry into dialog | ||||||
| 			View ees = (View)sender.Parent; | 			View ees = (View)sender.Parent; | ||||||
| @@ -1572,6 +1578,7 @@ namespace keepass2android | |||||||
|  |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | #if !NO_QR_SCANNER | ||||||
|     public class SuccessListener : Object, IOnSuccessListener |     public class SuccessListener : Object, IOnSuccessListener | ||||||
|     { |     { | ||||||
|         private readonly Action<Barcode> _onSuccess; |         private readonly Action<Barcode> _onSuccess; | ||||||
| @@ -1601,6 +1608,7 @@ namespace keepass2android | |||||||
|             _onFailure?.Invoke(e); |             _onFailure?.Invoke(e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| 	public class DefaultEdit : EditModeBase | 	public class DefaultEdit : EditModeBase | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ using Android.App; | |||||||
| using Android.App.Admin; | using Android.App.Admin; | ||||||
| using Android.Content; | using Android.Content; | ||||||
| using Android.Content.PM; | using Android.Content.PM; | ||||||
|  | using Android.Content.Res; | ||||||
| using Android.Graphics; | using Android.Graphics; | ||||||
| using Android.OS; | using Android.OS; | ||||||
| using Android.Preferences; | using Android.Preferences; | ||||||
| @@ -66,8 +67,13 @@ namespace keepass2android | |||||||
|             Resource.Id.cb_exclude_lookalike |             Resource.Id.cb_exclude_lookalike | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |          | ||||||
|  |  | ||||||
| 		PasswordFont _passwordFont = new PasswordFont(); | 		PasswordFont _passwordFont = new PasswordFont(); | ||||||
|  |  | ||||||
|  |         private static object _popularPasswordsLock = new object(); | ||||||
|  |         private static bool _popularPasswordsInitialized = false; | ||||||
|  |  | ||||||
|  |  | ||||||
|         private ActivityDesign _design; |         private ActivityDesign _design; | ||||||
| 	    public GeneratePasswordActivity() | 	    public GeneratePasswordActivity() | ||||||
| @@ -302,6 +308,10 @@ namespace keepass2android | |||||||
|  |  | ||||||
|  |  | ||||||
|             EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit); |             EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit); | ||||||
|  |             txtPasswordToSet.TextChanged += (sender, args) => | ||||||
|  |             { | ||||||
|  |                 Task.Run(() => UpdatePasswordStrengthEstimate(txtPasswordToSet.Text)); | ||||||
|  |             }; | ||||||
|  |  | ||||||
|             _passwordFont.ApplyTo(txtPasswordToSet); |             _passwordFont.ApplyTo(txtPasswordToSet); | ||||||
|  |  | ||||||
| @@ -467,17 +477,48 @@ namespace keepass2android | |||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|             String password = ""; |             String password = ""; | ||||||
|             uint passwordBits = 0; |              | ||||||
|              |              | ||||||
|             Task.Run(() => |             Task.Run(() => | ||||||
|             { |             { | ||||||
|                 password = GeneratePassword(); |                 password = GeneratePassword(); | ||||||
|                 passwordBits = QualityEstimation.EstimatePasswordBits(password.ToCharArray()); |  | ||||||
|                 RunOnUiThread(() => |                 RunOnUiThread(() => | ||||||
|                 { |                 { | ||||||
|                     EditText txtPassword = (EditText)FindViewById(Resource.Id.password_edit); |                     EditText txtPassword = (EditText)FindViewById(Resource.Id.password_edit); | ||||||
|                     txtPassword.Text = password; |                     txtPassword.Text = password; | ||||||
|  |                     UpdateProfileSpinnerSelection(); | ||||||
|  |                 }); | ||||||
|  |             }); | ||||||
|              |              | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void UpdatePasswordStrengthEstimate(string password) | ||||||
|  |         { | ||||||
|  |             lock (_popularPasswordsLock) | ||||||
|  |             { | ||||||
|  |                 if (!_popularPasswordsInitialized) | ||||||
|  |                 { | ||||||
|  |  | ||||||
|  |                     using (StreamReader sr = new StreamReader(Assets.Open("MostPopularPasswords.txt"))) | ||||||
|  |                     { | ||||||
|  |                         var bytes = default(byte[]); | ||||||
|  |                         using (var memstream = new MemoryStream()) | ||||||
|  |                         { | ||||||
|  |                             sr.BaseStream.CopyTo(memstream); | ||||||
|  |                             bytes = memstream.ToArray(); | ||||||
|  |                         } | ||||||
|  |                         PopularPasswords.Add(bytes, false); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                      | ||||||
|  |             } | ||||||
|  |             uint passwordBits = QualityEstimation.EstimatePasswordBits(password.ToCharArray()); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             RunOnUiThread(() => | ||||||
|  |             { | ||||||
|                 var progressBar = FindViewById<ProgressBar>(Resource.Id.pb_password_strength); |                 var progressBar = FindViewById<ProgressBar>(Resource.Id.pb_password_strength); | ||||||
|  |  | ||||||
|                 progressBar.Progress = (int)passwordBits; |                 progressBar.Progress = (int)passwordBits; | ||||||
| @@ -503,13 +544,7 @@ namespace keepass2android | |||||||
|                     PorterDuff.Mode.SrcIn)); |                     PorterDuff.Mode.SrcIn)); | ||||||
|  |  | ||||||
|                 FindViewById<TextView>(Resource.Id.tv_password_strength).Text = " " + passwordBits + " bits"; |                 FindViewById<TextView>(Resource.Id.tv_password_strength).Text = " " + passwordBits + " bits"; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|                     UpdateProfileSpinnerSelection(); |  | ||||||
|             }); |             }); | ||||||
|             }); |  | ||||||
|              |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void UpdateProfileSpinnerSelection() |         private void UpdateProfileSpinnerSelection() | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
| 			android:versionCode="211" |       android:versionCode="218" | ||||||
| 			android:versionName="1.12-r7" |       android:versionName="1.12-r9d" | ||||||
|       package="keepass2android.keepass2android" |       package="keepass2android.keepass2android" | ||||||
|       xmlns:tools="http://schemas.android.com/tools" |       xmlns:tools="http://schemas.android.com/tools" | ||||||
|       android:installLocation="auto"> |       android:installLocation="auto"> | ||||||
| @@ -46,12 +46,10 @@ | |||||||
|  |  | ||||||
|   <permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher" android:label="KP2A entry search" android:name="keepass2android.keepass2android.permission.KP2aInternalSearch" android:protectionLevel="signature" /> |   <permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher" android:label="KP2A entry search" android:name="keepass2android.keepass2android.permission.KP2aInternalSearch" android:protectionLevel="signature" /> | ||||||
|   <permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android.permission.Kp2aChooseAutofill" android:protectionLevel="signature" /> |   <permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android.permission.Kp2aChooseAutofill" android:protectionLevel="signature" /> | ||||||
|  |  | ||||||
|   <application android:label="keepass2android" |   <application android:label="keepass2android" | ||||||
|     android:icon="@mipmap/ic_launcher_online" |     android:icon="@mipmap/ic_launcher_online" | ||||||
|     android:roundIcon="@mipmap/ic_launcher_online_round" |     android:roundIcon="@mipmap/ic_launcher_online_round" | ||||||
|     android:networkSecurityConfig="@xml/network_security_config" |     android:networkSecurityConfig="@xml/network_security_config" | ||||||
|  |  | ||||||
|   > |   > | ||||||
|  |  | ||||||
|     <meta-data |     <meta-data | ||||||
| @@ -107,16 +105,15 @@ | |||||||
|       </intent-filter> |       </intent-filter> | ||||||
|     </activity> |     </activity> | ||||||
|     <activity android:name="keepass2android.softkeyboard.InputLanguageSelection" |     <activity android:name="keepass2android.softkeyboard.InputLanguageSelection" | ||||||
|                |  | ||||||
|                android:exported="true"> |                android:exported="true"> | ||||||
|       <!-- android:label="@string/language_selection_title" TODO  --> |  | ||||||
|       <intent-filter> |       <intent-filter> | ||||||
|         <action android:name="android.intent.action.MAIN"/> |         <action android:name="android.intent.action.MAIN"/> | ||||||
|         <action android:name="keepass2android.softkeyboard.INPUT_LANGUAGE_SELECTION"/> |         <action android:name="keepass2android.softkeyboard.INPUT_LANGUAGE_SELECTION"/> | ||||||
|         <category android:name="android.intent.category.DEFAULT" /> |         <category android:name="android.intent.category.DEFAULT" /> | ||||||
|       </intent-filter> |       </intent-filter> | ||||||
|     </activity> |     </activity> | ||||||
|     <activity android:configChanges="orientation|keyboard|keyboardHidden" android:label="@string/app_name" android:theme="@style/Kp2aTheme_BlueNoActionBar" android:name="keepass2android.SelectCurrentDbActivity"  android:windowSoftInputMode="adjustResize" android:exported="true"> |     <activity android:configChanges="orientation|keyboard|keyboardHidden" android:label="@string/app_name" android:theme="@style/Kp2aTheme_BlueNoActionBar" android:name="keepass2android.SelectCurrentDbActivity" android:windowSoftInputMode="adjustResize" | ||||||
|  |     android:exported="true"> | ||||||
|       <intent-filter android:label="@string/app_name"> |       <intent-filter android:label="@string/app_name"> | ||||||
|         <action android:name="android.intent.action.VIEW" /> |         <action android:name="android.intent.action.VIEW" /> | ||||||
|         <category android:name="android.intent.category.DEFAULT" /> |         <category android:name="android.intent.category.DEFAULT" /> | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
| 			android:versionCode="200"  |       android:versionCode="218" | ||||||
| 			android:versionName="1.11-r0"  |       android:versionName="1.12-r9d" | ||||||
|       package="keepass2android.keepass2android_nonet" |       package="keepass2android.keepass2android_nonet" | ||||||
|       xmlns:tools="http://schemas.android.com/tools" |       xmlns:tools="http://schemas.android.com/tools" | ||||||
|       android:installLocation="auto"> |       android:installLocation="auto"> | ||||||
| @@ -11,6 +11,9 @@ | |||||||
|     <!-- Specific intents and packages we query for (required since Android 11) --> |     <!-- Specific intents and packages we query for (required since Android 11) --> | ||||||
|     <package android:name="keepass2android.plugin.keyboardswap2" /> |     <package android:name="keepass2android.plugin.keyboardswap2" /> | ||||||
|     <package android:name="keepass2android.AncientIconSet" /> |     <package android:name="keepass2android.AncientIconSet" /> | ||||||
|  |     <package android:name="keepass2android.plugin.qr" /> | ||||||
|  |     <package android:name="it.andreacioni.kp2a.plugin.keelink" /> | ||||||
|  |     <package android:name="com.inputstick.apps.kp2aplugin" /> | ||||||
|     <package android:name="com.dropbox.android" /> |     <package android:name="com.dropbox.android" /> | ||||||
|  |  | ||||||
|     <intent> |     <intent> | ||||||
| @@ -39,13 +42,13 @@ | |||||||
|       <action android:name="android.intent.action.VIEW" /> |       <action android:name="android.intent.action.VIEW" /> | ||||||
|     </intent> |     </intent> | ||||||
|   </queries> |   </queries> | ||||||
| 			 |  | ||||||
|   <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" /> |   <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" /> | ||||||
|  |  | ||||||
|   <permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher_offline" android:label="KP2A entry search" android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" android:protectionLevel="signature" /> |   <permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher_offline" android:label="KP2A entry search" android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" android:protectionLevel="signature" /> | ||||||
|   <permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher_offline" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android_nonet.permission.Kp2aChooseAutofill" android:protectionLevel="signature" /> |   <permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher_offline" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android_nonet.permission.Kp2aChooseAutofill" android:protectionLevel="signature" /> | ||||||
| 	<application  |   <application android:label="keepass2android" | ||||||
| 		android:label="keepass2android"  |  | ||||||
|     android:icon="@mipmap/ic_launcher_offline" |     android:icon="@mipmap/ic_launcher_offline" | ||||||
|  |     android:roundIcon="@mipmap/ic_launcher_offline_round" | ||||||
|     android:networkSecurityConfig="@xml/network_security_config" |     android:networkSecurityConfig="@xml/network_security_config" | ||||||
|   > |   > | ||||||
|  |  | ||||||
| @@ -78,7 +81,6 @@ | |||||||
|       </intent-filter> |       </intent-filter> | ||||||
|     </activity> |     </activity> | ||||||
|     <activity android:name="keepass2android.softkeyboard.InputLanguageSelection" |     <activity android:name="keepass2android.softkeyboard.InputLanguageSelection" | ||||||
|                 android:label="@string/language_selection_title" |  | ||||||
|                android:exported="true"> |                android:exported="true"> | ||||||
|       <intent-filter> |       <intent-filter> | ||||||
|         <action android:name="android.intent.action.MAIN"/> |         <action android:name="android.intent.action.MAIN"/> | ||||||
| @@ -86,7 +88,6 @@ | |||||||
|         <category android:name="android.intent.category.DEFAULT" /> |         <category android:name="android.intent.category.DEFAULT" /> | ||||||
|       </intent-filter> |       </intent-filter> | ||||||
|     </activity> |     </activity> | ||||||
|   |  | ||||||
|     <activity android:configChanges="orientation|keyboard|keyboardHidden" android:label="@string/app_name" android:theme="@style/Kp2aTheme_BlueNoActionBar" android:name="keepass2android.SelectCurrentDbActivity" android:windowSoftInputMode="adjustResize" |     <activity android:configChanges="orientation|keyboard|keyboardHidden" android:label="@string/app_name" android:theme="@style/Kp2aTheme_BlueNoActionBar" android:name="keepass2android.SelectCurrentDbActivity" android:windowSoftInputMode="adjustResize" | ||||||
|     android:exported="true"> |     android:exported="true"> | ||||||
|       <intent-filter android:label="@string/app_name"> |       <intent-filter android:label="@string/app_name"> | ||||||
| @@ -218,15 +219,6 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik | |||||||
|         <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdb" /> |         <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdb" /> | ||||||
|       </intent-filter> |       </intent-filter> | ||||||
|  |  | ||||||
|       <intent-filter> |  | ||||||
|         <action android:name="android.intent.action.VIEW"/> |  | ||||||
|         <category android:name="android.intent.category.BROWSABLE"/> |  | ||||||
|         <data |  | ||||||
|           android:scheme="https" |  | ||||||
|           android:host="my.yubico.com" |  | ||||||
|           android:pathPrefix="/neo"/> |  | ||||||
|       </intent-filter> |  | ||||||
| 	   |  | ||||||
|       <intent-filter android:label="@string/kp2a_findUrl"> |       <intent-filter android:label="@string/kp2a_findUrl"> | ||||||
|         <action android:name="android.intent.action.SEND" /> |         <action android:name="android.intent.action.SEND" /> | ||||||
|         <category android:name="android.intent.category.DEFAULT" /> |         <category android:name="android.intent.category.DEFAULT" /> | ||||||
| @@ -254,14 +246,18 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik | |||||||
|   </application> |   </application> | ||||||
|   <uses-permission android:name="android.permission.VIBRATE" /> |   <uses-permission android:name="android.permission.VIBRATE" /> | ||||||
|   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | ||||||
|  |   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> | ||||||
|  |   <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | ||||||
|  |   <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" /> | ||||||
|  |   <uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalFileBrowsing" /> | ||||||
|  |   <uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" /> | ||||||
|   <uses-permission android:name="android.permission.USE_FINGERPRINT" /> |   <uses-permission android:name="android.permission.USE_FINGERPRINT" /> | ||||||
|   <uses-permission android:name="android.permission.USE_BIOMETRIC" /> |   <uses-permission android:name="android.permission.USE_BIOMETRIC" /> | ||||||
|   <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> |   <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> | ||||||
|  |  | ||||||
|   <uses-feature android:name="android.hardware.camera" android:required="false" /> |   <uses-feature android:name="android.hardware.camera" android:required="false" /> | ||||||
|  |  | ||||||
|   <uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalFileBrowsing" /> |   <!-- Samsung Pass permission --> | ||||||
|   <uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" /> |   <uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" /> | ||||||
|   <uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" /> |   <uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" /> | ||||||
| 	 |  | ||||||
| </manifest> | </manifest> | ||||||
|   | |||||||
| @@ -1423,6 +1423,8 @@ namespace keepass2android | |||||||
| 			if (cbQuickUnlock == null) | 			if (cbQuickUnlock == null) | ||||||
| 				throw new NullPointerException("cpQuickUnlock"); | 				throw new NullPointerException("cpQuickUnlock"); | ||||||
| 			App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked); | 			App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked); | ||||||
|  |             App.Kp2a.ScreenLockWasEnabledWhenOpeningDatabase = | ||||||
|  |                 (((KeyguardManager)GetSystemService(Context.KeyguardService)!)!).IsDeviceSecure; | ||||||
|  |  | ||||||
|             if ((_loadDbFileTask != null) &&  (App.Kp2a.OfflineMode != _loadDbTaskOffline)) |             if ((_loadDbFileTask != null) &&  (App.Kp2a.OfflineMode != _loadDbTaskOffline)) | ||||||
| 			{ | 			{ | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ using Android.Widget; | |||||||
| using Android.Content.PM; | using Android.Content.PM; | ||||||
| using KeePassLib.Keys; | using KeePassLib.Keys; | ||||||
| using Android.Preferences; | using Android.Preferences; | ||||||
|  | using Android.Provider; | ||||||
| using Android.Runtime; | using Android.Runtime; | ||||||
|  |  | ||||||
| using Android.Views.InputMethods; | using Android.Views.InputMethods; | ||||||
| @@ -162,6 +163,29 @@ namespace keepass2android | |||||||
|             if (bundle != null) |             if (bundle != null) | ||||||
|                 numFailedAttempts = bundle.GetInt(NumFailedAttemptsKey, 0); |                 numFailedAttempts = bundle.GetInt(NumFailedAttemptsKey, 0); | ||||||
|  |  | ||||||
|  |             FindViewById(Resource.Id.QuickUnlock_buttonEnableLock).Click += (object sender, EventArgs e) => | ||||||
|  |             { | ||||||
|  | 				Intent intent = new Intent(Settings.ActionSecuritySettings); | ||||||
|  |                 StartActivity(intent); | ||||||
|  |  | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             FindViewById(Resource.Id.QuickUnlock_buttonCloseDb).Click += (object sender, EventArgs e) => | ||||||
|  |             { | ||||||
|  |                 App.Kp2a.Lock(false); | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             if (App.Kp2a.ScreenLockWasEnabledWhenOpeningDatabase == false) | ||||||
|  |             { | ||||||
|  | 				FindViewById(Resource.Id.QuickUnlockForm).Visibility = ViewStates.Gone; | ||||||
|  |                 FindViewById(Resource.Id.QuickUnlockBlocked).Visibility = ViewStates.Visible; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 FindViewById(Resource.Id.QuickUnlockForm).Visibility = ViewStates.Visible; | ||||||
|  |                 FindViewById(Resource.Id.QuickUnlockBlocked).Visibility = ViewStates.Gone; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -79,6 +79,12 @@ android:paddingRight="16dp" | |||||||
|           android:paddingTop="16dp"> |           android:paddingTop="16dp"> | ||||||
|  |  | ||||||
|          |          | ||||||
|  |         <LinearLayout | ||||||
|  |           android:layout_width="wrap_content" | ||||||
|  |           android:layout_height="wrap_content" | ||||||
|  |           android:orientation="horizontal" | ||||||
|  |           android:id="@+id/QuickUnlockForm"> | ||||||
|  |  | ||||||
|           <TextView |           <TextView | ||||||
|             android:id="@+id/QuickUnlock_label" |             android:id="@+id/QuickUnlock_label" | ||||||
|             android:text="@string/QuickUnlock_label" |             android:text="@string/QuickUnlock_label" | ||||||
| @@ -88,11 +94,6 @@ android:paddingRight="16dp" | |||||||
|             android:textSize="14sp" |             android:textSize="14sp" | ||||||
|           /> |           /> | ||||||
|  |  | ||||||
|         <LinearLayout |  | ||||||
|           android:layout_width="wrap_content" |  | ||||||
|           android:layout_height="wrap_content" |  | ||||||
|           android:orientation="horizontal"> |  | ||||||
|  |  | ||||||
|           <EditText |           <EditText | ||||||
|             android:inputType="textPassword" |             android:inputType="textPassword" | ||||||
|             android:layout_width="wrap_content" |             android:layout_width="wrap_content" | ||||||
| @@ -121,6 +122,60 @@ android:paddingRight="16dp" | |||||||
|  |  | ||||||
|         </LinearLayout> |         </LinearLayout> | ||||||
|  |  | ||||||
|  |         <LinearLayout | ||||||
|  |           android:layout_width="match_parent" | ||||||
|  |           android:layout_height="wrap_content" | ||||||
|  |           android:orientation="vertical" | ||||||
|  |           android:background="@color/md_theme_secondaryContainer" | ||||||
|  |           android:id="@+id/QuickUnlockBlocked" | ||||||
|  |           android:padding="16dp" | ||||||
|  |           android:layout_gravity="center"> | ||||||
|  |  | ||||||
|  |           <TextView | ||||||
|  |             android:id="@+id/quick_unlock_blocked_title" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="wrap_content" | ||||||
|  |             android:text="@string/password_based_quick_unlock_not_available" | ||||||
|  |             android:textSize="16sp" | ||||||
|  |             android:textStyle="bold" | ||||||
|  |             android:gravity="center" | ||||||
|  |             android:paddingBottom="8dp"/> | ||||||
|  |  | ||||||
|  |           <TextView | ||||||
|  |             android:id="@+id/alert_message" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="wrap_content" | ||||||
|  |             android:text="@string/password_based_quick_unlock_not_available_text" | ||||||
|  |             android:textSize="16sp" | ||||||
|  |                             android:paddingBottom="8dp"/> | ||||||
|  |           <LinearLayout | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="wrap_content" | ||||||
|  |             android:orientation="vertical"> | ||||||
|  |             <Button   | ||||||
|  |               android:id="@+id/QuickUnlock_buttonEnableLock" | ||||||
|  |               android:layout_width="wrap_content" | ||||||
|  |               android:layout_height="wrap_content" | ||||||
|  |               android:layout_gravity="center" | ||||||
|  |               android:backgroundTint="@color/md_theme_secondary" | ||||||
|  |               android:textColor="@android:color/white" | ||||||
|  |               android:text="@string/enable_screen_lock" | ||||||
|  |               android:fontFamily="sans-serif-medium"   /> | ||||||
|  |  | ||||||
|  |               <Button   | ||||||
|  |               android:id="@+id/QuickUnlock_buttonCloseDb" | ||||||
|  |               android:layout_width="wrap_content" | ||||||
|  |               android:layout_height="wrap_content" | ||||||
|  |               android:layout_gravity="center" | ||||||
|  |               android:backgroundTint="@color/md_theme_secondary" | ||||||
|  |               android:textColor="@android:color/white" | ||||||
|  |               android:fontFamily="sans-serif-medium"   | ||||||
|  |               android:text="@string/QuickUnlock_lockButton" /> | ||||||
|  |              | ||||||
|  |             </LinearLayout> | ||||||
|  |              | ||||||
|  |  | ||||||
|  |           </LinearLayout> | ||||||
|        |        | ||||||
|         <View |         <View | ||||||
|         android:id="@+id/spacing" |         android:id="@+id/spacing" | ||||||
|   | |||||||
| @@ -93,6 +93,8 @@ | |||||||
|   <string name="disable_fingerprint_unlock">Disable Biometric Unlock</string> |   <string name="disable_fingerprint_unlock">Disable Biometric Unlock</string> | ||||||
|   <string name="enable_fingerprint_unlock">Enable full Biometric Unlock</string> |   <string name="enable_fingerprint_unlock">Enable full Biometric Unlock</string> | ||||||
|   <string name="enable_fingerprint_quickunlock">Enable Biometric Unlock for QuickUnlock</string> |   <string name="enable_fingerprint_quickunlock">Enable Biometric Unlock for QuickUnlock</string> | ||||||
|  |   <string name="password_based_quick_unlock_not_available">Password-based QuickUnlock not available</string> | ||||||
|  |   <string name="password_based_quick_unlock_not_available_text">QuickUnlock using a part of your password is blocked because screen lock is not activated on your device. This behavior is to protect you in case somebody watched you entering your QuickUnlock key.</string> | ||||||
|   <string name="fingerprint_unlock_failed">Biometric Unlock failed. Decryption key was invalidated by Android OS. This usually happens if a biometric authentication or security settings were changed. </string> |   <string name="fingerprint_unlock_failed">Biometric Unlock failed. Decryption key was invalidated by Android OS. This usually happens if a biometric authentication or security settings were changed. </string> | ||||||
|   <string name="fingerprint_disabled_wrong_masterkey">Unlocking the database failed: Invalid composite key. Biometric Unlock was disabled because apparently the stored master password is no longer valid. </string> |   <string name="fingerprint_disabled_wrong_masterkey">Unlocking the database failed: Invalid composite key. Biometric Unlock was disabled because apparently the stored master password is no longer valid. </string> | ||||||
|   <string name="fingerprint_reenable">Please re-enable Biometric Unlock for the new master password.</string> |   <string name="fingerprint_reenable">Please re-enable Biometric Unlock for the new master password.</string> | ||||||
| @@ -319,6 +321,7 @@ | |||||||
|   <string name="QuickUnlock_label_secure">Enter QuickUnlock code:</string> |   <string name="QuickUnlock_label_secure">Enter QuickUnlock code:</string> | ||||||
|   <string name="QuickUnlock_button">QuickUnlock!</string> |   <string name="QuickUnlock_button">QuickUnlock!</string> | ||||||
|   <string name="QuickUnlock_lockButton">Close database</string> |   <string name="QuickUnlock_lockButton">Close database</string> | ||||||
|  |   <string name="enable_screen_lock">Enable screen lock</string> | ||||||
|   <string name="QuickUnlockDefaultEnabled_title">Enable QuickUnlock by default</string> |   <string name="QuickUnlockDefaultEnabled_title">Enable QuickUnlock by default</string> | ||||||
|   <string name="QuickUnlockDefaultEnabled_summary">Defines whether QuickUnlock is enabled by default or not.</string> |   <string name="QuickUnlockDefaultEnabled_summary">Defines whether QuickUnlock is enabled by default or not.</string> | ||||||
|   <string name="ViewDatabaseSecure_title">Protect database display</string> |   <string name="ViewDatabaseSecure_title">Protect database display</string> | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <network-security-config> | <network-security-config> | ||||||
|   <base-config cleartextTrafficPermitted="true"> |   <base-config> | ||||||
|     <trust-anchors> |     <trust-anchors> | ||||||
|       <certificates src="system" /> |       <certificates src="system" /> | ||||||
|       <certificates src="user" /> |       <certificates src="user" /> | ||||||
|   | |||||||
| @@ -351,6 +351,9 @@ namespace keepass2android | |||||||
| 			QuickUnlockEnabled = enabled; | 			QuickUnlockEnabled = enabled; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		public bool ScreenLockWasEnabledWhenOpeningDatabase { get; set; } | ||||||
|  |  | ||||||
|  |  | ||||||
|         public bool QuickUnlockEnabled { get; private set; } |         public bool QuickUnlockEnabled { get; private set; } | ||||||
|  |  | ||||||
| 		public int QuickUnlockKeyLength { get; private set; } | 		public int QuickUnlockKeyLength { get; private set; } | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ | |||||||
|     <ApplicationVersion>1</ApplicationVersion> |     <ApplicationVersion>1</ApplicationVersion> | ||||||
|     <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> |     <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> | ||||||
|     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> |     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||||
|  |     <DefineConstants Condition="'$(Flavor)'=='NoNet'">NO_QR_SCANNER;EXCLUDE_JAVAFILESTORAGE;NoNet</DefineConstants> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <None Remove="Assets\fontawesome-webfont.ttf" /> |     <None Remove="Assets\fontawesome-webfont.ttf" /> | ||||||
| @@ -750,18 +751,18 @@ | |||||||
|     <PackageReference Include="Xamarin.AndroidX.Preference" Version="1.2.1.12" /> |     <PackageReference Include="Xamarin.AndroidX.Preference" Version="1.2.1.12" /> | ||||||
|     <PackageReference Include="Xamarin.Google.Android.Material" Version="1.12.0.2" /> |     <PackageReference Include="Xamarin.Google.Android.Material" Version="1.12.0.2" /> | ||||||
|     <PackageReference Include="Xamarin.Google.Guava" Version="33.4.0.1" /> |     <PackageReference Include="Xamarin.Google.Guava" Version="33.4.0.1" /> | ||||||
|     <PackageReference Include="Xamarin.GooglePlayServices.Auth" Version="121.3.0.1" /> |     <PackageReference Include="Xamarin.GooglePlayServices.Auth" Version="121.3.0.1" Condition="'$(Flavor)'!='NoNet'" /> | ||||||
|     <PackageReference Include="Xamarin.GooglePlayServices.Base" Version="118.5.0.4" /> |     <PackageReference Include="Xamarin.GooglePlayServices.Base" Version="118.5.0.4" Condition="'$(Flavor)'!='NoNet'" /> | ||||||
|     <PackageReference Include="Xamarin.GooglePlayServices.Code.Scanner" Version="116.1.0.10" /> |     <PackageReference Include="Xamarin.GooglePlayServices.Code.Scanner" Version="116.1.0.10" Condition="'$(Flavor)'!='NoNet'" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj" /> |     <ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj" /> | ||||||
|     <ProjectReference Include="..\DropboxBinding\DropboxBinding.csproj" /> |     <ProjectReference Include="..\DropboxBinding\DropboxBinding.csproj" Condition="'$(Flavor)'!='NoNet'" /> | ||||||
|     <ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj" /> |     <ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj" Condition="'$(Flavor)'!='NoNet'" /> | ||||||
|     <ProjectReference Include="..\Kp2aAutofillParser\Kp2aAutofillParser.csproj" /> |     <ProjectReference Include="..\Kp2aAutofillParser\Kp2aAutofillParser.csproj" /> | ||||||
|     <ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj" /> |     <ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj" /> | ||||||
|     <ProjectReference Include="..\Kp2aKeyboardBinding\Kp2aKeyboardBinding.csproj" /> |     <ProjectReference Include="..\Kp2aKeyboardBinding\Kp2aKeyboardBinding.csproj" /> | ||||||
|     <ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" /> |     <ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" Condition="'$(Flavor)'!='NoNet'" /> | ||||||
|     <ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj" /> |     <ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj" /> | ||||||
|     <ProjectReference Include="..\ZlibAndroid\ZlibAndroid.csproj" /> |     <ProjectReference Include="..\ZlibAndroid\ZlibAndroid.csproj" /> | ||||||
|     <ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj" /> |     <ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj" /> | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ using KeePassLib.Serialization; | |||||||
|  |  | ||||||
| namespace keepass2android | namespace keepass2android | ||||||
| { | { | ||||||
|     [Activity] |     [Activity(Theme = "@style/Kp2aTheme_ActionBar")] | ||||||
|     public class ExportKeyfileActivity : LockCloseActivity |     public class ExportKeyfileActivity : LockCloseActivity | ||||||
|     { |     { | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user