Compare commits

...

31 Commits

Author SHA1 Message Date
Philipp Crocoll
057a7e2f7a another nonet fix 2025-07-15 13:12:18 +02:00
Philipp Crocoll
cfb5098b38 add support for transactional upload 2025-07-15 12:18:45 +02:00
Philipp Crocoll
913222d7cb allow chunked uploads, closes https://github.com/PhilippC/keepass2android/issues/2777 2025-07-15 11:07:40 +02:00
Philipp Crocoll
3e6d86c206 correctly check if an item is a folder or file. closes https://github.com/PhilippC/keepass2android/issues/2589 2025-07-15 09:10:41 +02:00
Philipp Crocoll
d6ce2a32e9 allow manually triggering a release workflow run 2025-07-15 08:30:17 +02:00
Philipp Crocoll
21f1c8404c update workflows to not create signed apks during build but only in release. Create releases as drafts. 2025-07-15 08:14:52 +02:00
Philipp Crocoll
092b8689b8 fix build-artifact paths 2025-07-08 21:35:51 +02:00
Philipp Crocoll
3d3ba45cb1 extract keystore for subsequent build step 2025-07-08 18:23:02 +02:00
Philipp Crocoll
b380100307 Manifest and changelog for v1.13 2025-07-08 17:57:52 +02:00
Philipp Crocoll
4c5ddd59d8 build signed apk on every build of master branch 2025-07-08 17:57:37 +02:00
PhilippC
5ed183f318 Merge pull request #2929 from PhilippC/l10n_master3
New Crowdin updates
2025-07-08 17:56:50 +02:00
PhilippC
9c27fd3e78 Merge pull request #2938 from PhilippC/security/audit_suggestions
Security suggestions from Audit
2025-07-08 17:52:16 +02:00
Philipp Crocoll
62c361feb0 revert unintentional change 2025-07-08 17:03:32 +02:00
Philipp Crocoll
0ee2495528 disable password-based QuickUnlock when device is not protected by screen lock 2025-07-08 16:54:45 +02:00
Philipp Crocoll
7dc635a625 Update KeePass2 code for password quality estimation; add and use list of most popular passwords to account for NIST recommendation of using "blocklists" 2025-07-08 12:09:59 +02:00
Philipp Crocoll
e15112c3b4 disable cleartextTrafficPermitted. default to https for links. 2025-07-08 10:35:50 +02:00
PhilippC
1d85fffb18 New translations strings.xml (Chinese Simplified) 2025-07-07 18:44:26 +02:00
PhilippC
5e2f29e737 New translations strings.xml (Chinese Simplified) 2025-07-07 17:20:28 +02:00
Philipp Crocoll
89a09ea142 set version to 1.12-r9d 2025-07-05 14:17:07 +02:00
Philipp Crocoll
628c0d2c19 enable creation of a release 2025-07-05 14:16:17 +02:00
Philipp Crocoll
584feabe44 build process: add previously missing change; fix error in build.yml, more verbose output in release.yml 2025-07-05 13:34:01 +02:00
Philipp Crocoll
ae2cfde897 change makefile to no longer use msbuild but always dotnet 2025-07-05 13:10:21 +02:00
Philipp Crocoll
42c66670b8 release process: make find calls to run in bash 2025-07-05 12:12:49 +02:00
Philipp Crocoll
288539b902 make output more verbose 2025-07-05 11:09:55 +02:00
Philipp Crocoll
61fd32f121 avoid building the "full" apk when calling apk_split 2025-07-05 09:39:54 +02:00
Philipp Crocoll
43108ec4a6 remove ls step in release 2025-07-05 08:35:58 +02:00
PhilippC
426fbc2510 New translations strings.xml (Polish) 2025-07-04 23:10:33 +02:00
PhilippC
e89a961c02 New translations strings.xml (Polish) 2025-07-04 22:15:11 +02:00
PhilippC
766c29b7a9 New translations strings.xml (Spanish) 2025-07-03 14:39:21 +02:00
PhilippC
c4206e58bf New translations strings.xml (German) 2025-06-24 15:34:08 +02:00
PhilippC
630ededf3b New translations strings.xml (German) 2025-06-24 13:51:08 +02:00
38 changed files with 13314 additions and 2493 deletions

View File

@@ -78,7 +78,7 @@ jobs:
# - name: Build keepass2android (net)
# run: |
# make msbuild Flavor=Net
# make dotnetbuild Flavor=Net
# - name: Build APK (net)
# run: |
@@ -96,7 +96,7 @@ jobs:
# - name: Build keepass2android (nonet)
# run: |
# make msbuild Flavor=NoNet
# make dotnetbuild Flavor=NoNet
# - name: Build APK (nonet)
# run: |
@@ -212,7 +212,7 @@ jobs:
# - name: Build keepass2android (net)
# run: |
# make msbuild Flavor=Net
# make dotnetbuild Flavor=Net
# - name: Build APK (net)
# run: |
@@ -230,7 +230,7 @@ jobs:
# - name: Build keepass2android (nonet)
# run: |
# make msbuild Flavor=NoNet
# make dotnetbuild Flavor=NoNet
# - name: Build APK (nonet)
# run: |
@@ -279,7 +279,7 @@ jobs:
with:
minimum-size: 8GB
- name: Add msbuild to PATH
- name: Add dotnetbuild to PATH
uses: microsoft/setup-msbuild@v2
# If we want to also have nmake, use this instead
#uses: ilammy/msvc-dev-cmd@v1
@@ -322,18 +322,24 @@ jobs:
- name: Build keepass2android (net)
run: |
make msbuild Flavor=Net
make dotnetbuild Flavor=Net
- name: Build APK (net)
if: github.ref == 'refs/heads/master'
env:
DropboxAppKey: ${{ secrets.DROPBOX_APP_KEY }}
DropboxAppSecret: ${{ secrets.DROPBOX_APP_SECRET }}
DropboxAppFolderAppKey: ${{ secrets.DROPBOX_APP_FOLDER_APP_KEY }}
DropboxAppFolderAppSecret: ${{ secrets.DROPBOX_APP_FOLDER_APP_SECRET }}
run: |
make apk Flavor=Net
make apk Configuration=Release Flavor=Net
- name: Archive production artifacts (net)
uses: actions/upload-artifact@v4
with:
name: signed APK ('net' built on ${{ github.job }})
name: archive APK ('net' built on ${{ github.job }})
path: |
src/keepass2android/bin/*/*-Signed.apk
src/keepass2android-app/bin/Release/net8.0-android/publish/*.apk
- name: Select the manifest
run: |
@@ -344,7 +350,7 @@ jobs:
- name: Build keepass2android (nonet)
run: |
make msbuild Flavor=NoNet
make dotnetbuild Flavor=NoNet
- name: Test Autofill
working-directory: ./src/Kp2aAutofillParser.Tests
@@ -357,7 +363,7 @@ jobs:
- name: Archive production artifacts (nonet)
uses: actions/upload-artifact@v4
with:
name: signed APK ('nonet' built on ${{ github.job }})
name: archive APK ('nonet' built on ${{ github.job }})
path: |
src/keepass2android/bin/*/*-Signed.apk
src/keepass2android-app/bin/Release/net8.0-android/publish/*.apk

View File

@@ -6,6 +6,7 @@ on:
push:
tags:
- "v1.*"
workflow_dispatch: # Allows manual triggering of the workflow
jobs:
build-release:
@@ -54,7 +55,7 @@ jobs:
with:
minimum-size: 8GB
- name: Add msbuild to PATH
- name: Add msbuild/dotnet to PATH
uses: microsoft/setup-msbuild@v2
# If we want to also have nmake, use this instead
#uses: ilammy/msvc-dev-cmd@v1
@@ -78,17 +79,35 @@ jobs:
run: |
make java
- name: List apks
run: find . -type f -name "*.apk"
shell: bash
- name: Update dotnet workloads
run: |
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)
env:
KeyStore: "${{ github.workspace }}/kp2a.keystore"
@@ -102,23 +121,28 @@ jobs:
run: |
make ${{ matrix.target }} Configuration=Release Flavor=${{ matrix.flavor }}
- name: List files
- name: List apks
run: find . -type f -name "*.apk"
shell: bash
run: |
ls src/keepass2android-app/bin/Release/net8.0-android/publish/*.apk
ls src/keepass2android-app/bin/Release/net8.0-android/*/publish/*.apk
- name: Archive production artifacts
uses: actions/upload-artifact@v4
with:
name: keepass2android_${{ matrix.target }}_${{ matrix.flavor }}
# the first line is for "apk" target, the second line is for "apk_split" target
path: |
src/keepass2android-app/bin/Release/net8.0-android/publish/*.apk
src/keepass2android-app/bin/Release/net8.0-android/*/publish/*.apk
#- name: Upload APK to GitHub Release
# uses: softprops/action-gh-release@v2
# if: github.ref_type == 'tag'
# with:
# files: |
# 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
uses: softprops/action-gh-release@v2
if: github.ref_type == 'tag'
with:
draft: true
files: |
src/keepass2android-app/bin/Release/net8.0-android/publish/*.apk
src/keepass2android-app/bin/Release/net8.0-android/*/publish/*.apk

View File

@@ -4,10 +4,10 @@
# 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='
# of msbuild command.
# of dotnetbuild command.
#
# append the Flavor variable to 'make' call with value to use in '/p:Flavor='
# of msbuild command.
# of dotnetbuild command.
#
# Example:
# make Configuration=Release Flavor=NoNet
@@ -18,7 +18,7 @@
# - native: build the native libs
# - java: build the java libs
# - nuget: restore NuGet packages
# - msbuild: build the project
# - dotnetbuild: build the project
# - apk: same as all
# - 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_java: call clean target of java libs
# - 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 )
# On linux use xabuild, on Windows use MSBuild.exe, otherwise (macos?) use msbuild.
ifeq ($(detected_OS),Linux)
MSBUILD_binary := dotnet
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary)) publish
DOTNET_binary := dotnet
DOTNET := $(shell $(WHICH) $(DOTNET_binary))
else ifeq ($(detected_OS),Windows)
MSBUILD_binary := dotnet
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary) 2> nul) publish
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
DOTNET_binary := dotnet
DOTNET := $(shell $(WHICH) $(DOTNET_binary) 2> nul)
else
MSBUILD_binary := msbuild
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary))
DOTNET_binary := dotnet
DOTNET := $(shell $(WHICH) $(DOTNET_binary))
endif
ifeq ($(MSBUILD),)
ifeq ($(DOTNET),)
$(info )
$(info '$(MSBUILD_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
$(info '$(DOTNET_binary)' binary could not be found. Check it is in your PATH.)
$(error )
endif
$(info MSBUILD: $(MSBUILD))
$(info DOTNET: $(DOTNET))
$(info )
ifeq ($(ANDROID_SDK_ROOT),)
@@ -117,7 +95,7 @@ endif
$(info ANDROID_NDK_ROOT: $(ANDROID_NDK_ROOT))
ifneq ($(Configuration),)
MSBUILD_PARAM = -p:Configuration="$(Configuration)"
DOTNET_PARAM = -p:Configuration="$(Configuration)"
else
$(warning Configuration environment variable not set.)
endif
@@ -127,7 +105,7 @@ CREATE_MANIFEST_LINK :=
MANIFEST_FILE :=
ifneq ($(Flavor),)
MSBUILD_PARAM += -p:Flavor="$(Flavor)"
DOTNET_PARAM += -p:Flavor="$(Flavor)"
ifneq ($(Flavor),)
ifeq ($(Flavor),Debug)
MANIFEST_FILE := AndroidManifest_debug.xml
@@ -152,7 +130,7 @@ else
endif
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
ifeq ($(detected_OS),Windows)
@@ -176,7 +154,7 @@ endif
# Recursive wildcard: https://stackoverflow.com/a/18258352
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 )
@@ -254,7 +232,7 @@ OUTPUT_PluginQR = src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/Keepa
.PHONY: native $(NATIVE_COMPONENTS) clean_native $(NATIVE_CLEAN_TARGETS) \
java $(JAVA_COMPONENTS) clean_java $(JAVA_CLEAN_TARGETS) \
nuget clean_nuget \
msbuild clean_msbuild \
dotnetbuild clean_dotnet \
apk all clean
all: apk
@@ -303,7 +281,7 @@ ifeq ($(shell $(WHICH) nuget),)
endif
$(RMFILE) stamp.nuget_*
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)
manifestlink:
@@ -313,20 +291,20 @@ manifestlink:
#####
msbuild: 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
dotnetbuild: manifestlink native java nuget
$(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
$(MSBUILD) src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(MSBUILD_PARAM) -p:Platform=AnyCPU -m
apk: 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
apk_split: msbuild
$(MSBUILD) src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(MSBUILD_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-arm
$(MSBUILD) src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(MSBUILD_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-arm64
$(MSBUILD) src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(MSBUILD_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-x86
$(MSBUILD) src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(MSBUILD_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-x64
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: msbuild
build_all: dotnetbuild
##### Cleanup targets
@@ -370,10 +348,10 @@ else
endif
$(RMFILE) stamp.nuget_*
clean_msbuild:
$(MSBUILD) src/KeePass.sln -target:clean $(MSBUILD_PARAM)
clean_dotnet:
$(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
ifneq ("$(wildcard ./allow_git_clean)","")

View File

@@ -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).
# 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.
* [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?
Please see the [wiki](https://github.com/PhilippC/keepass2android/wiki/Documentation) for further information.

View File

@@ -1,6 +1,6 @@
/*
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
it under the terms of the GNU General Public License as published by
@@ -40,8 +40,8 @@ namespace KeePassLib.Cryptography
Null = 0,
/// <summary>
/// A variant of the ARCFour algorithm (RC4 incompatible).
/// </summary>
/// A variant of the ArcFour algorithm (RC4 incompatible).
/// Insecure; for backward compatibility only.
/// </summary>
ArcFourVariant = 1,
@@ -66,68 +66,72 @@ namespace KeePassLib.Cryptography
/// </summary>
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_j = 0;
private Salsa20Cipher m_salsa20 = null;
private ChaCha20Cipher m_chacha20 = null;
/// <summary>
/// Construct a new cryptographically secure random stream object.
/// </summary>
/// <param name="genAlgorithm">Algorithm to use.</param>
/// <param name="pbKey">Initialization key. Must not be <c>null</c> and
/// must contain at least 1 byte.</param>
/// <param name="a">Algorithm to use.</param>
/// <param name="pbKey">Initialization key. Must not be <c>null</c>
/// and must contain at least 1 byte.</param>
public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
{
if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
/// <exception cref="System.ArgumentNullException">Thrown if the
if (pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
int cbKey = pbKey.Length;
if(cbKey <= 0)
if (cbKey <= 0)
{
Debug.Assert(false); // Need at least one byte
throw new ArgumentOutOfRangeException("pbKey");
}
/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception>
m_crsAlgorithm = a;
/// <exception cref="System.ArgumentException">Thrown if the
if(a == CrsAlgorithm.ChaCha20)
m_alg = a;
if (a == CrsAlgorithm.ChaCha20)
{
byte[] pbKey32 = new byte[32];
byte[] pbIV12 = new byte[12];
/// <paramref name="pbKey" /> parameter contains no bytes or the
using(SHA512Managed h = new SHA512Managed())
m_pbKey = new byte[32];
m_pbIV = new byte[12];
using (SHA512Managed h = new SHA512Managed())
{
byte[] pbHash = h.ComputeHash(pbKey);
Array.Copy(pbHash, pbKey32, 32);
Array.Copy(pbHash, 32, pbIV12, 0, 12);
Array.Copy(pbHash, m_pbKey, 32);
Array.Copy(pbHash, 32, m_pbIV, 0, 12);
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);
byte[] pbIV8 = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
m_pbKey = CryptoUtil.HashSha256(pbKey);
m_pbIV = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
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)
{
// Fill the state linearly
m_pbState = new byte[256];
for(int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
for (int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
unchecked
{
byte j = 0, t;
int inxKey = 0;
for(int w = 0; w < 256; ++w) // Key setup
for (int w = 0; w < 256; ++w) // Key setup
{
j += (byte)(m_pbState[w] + pbKey[inxKey]);
@@ -136,7 +140,7 @@ namespace KeePassLib.Cryptography
m_pbState[j] = t;
++inxKey;
if(inxKey >= cbKey) inxKey = 0;
if (inxKey >= cbKey) inxKey = 0;
}
}
@@ -157,19 +161,24 @@ namespace KeePassLib.Cryptography
private void Dispose(bool disposing)
{
if(disposing)
if (disposing)
{
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
if (m_alg == CrsAlgorithm.ChaCha20)
m_chacha20.Dispose();
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
else if (m_alg == CrsAlgorithm.Salsa20)
m_salsa20.Dispose();
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
else if (m_alg == CrsAlgorithm.ArcFourVariant)
{
MemUtil.ZeroByteArray(m_pbState);
m_i = 0;
m_j = 0;
}
else { Debug.Assert(false); }
if (m_pbKey != null) MemUtil.ZeroByteArray(m_pbKey);
if (m_pbIV != null) MemUtil.ZeroByteArray(m_pbIV);
m_bDisposed = true;
}
}
@@ -180,23 +189,24 @@ namespace KeePassLib.Cryptography
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedCount)
{
if(uRequestedCount == 0) return MemUtil.EmptyByteArray;
if (m_bDisposed) throw new ObjectDisposedException(null);
if(uRequestedCount > (uint)int.MaxValue)
if (uRequestedCount == 0) return MemUtil.EmptyByteArray;
if (uRequestedCount > (uint)int.MaxValue)
throw new ArgumentOutOfRangeException("uRequestedCount");
int cb = (int)uRequestedCount;
byte[] pbRet = new byte[cb];
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
if (m_alg == CrsAlgorithm.ChaCha20)
m_chacha20.Encrypt(pbRet, 0, cb);
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
else if (m_alg == CrsAlgorithm.Salsa20)
m_salsa20.Encrypt(pbRet, 0, cb);
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
else if (m_alg == CrsAlgorithm.ArcFourVariant)
{
unchecked
{
for(int w = 0; w < cb; ++w)
for (int w = 0; w < cb; ++w)
{
++m_i;
m_j += m_pbState[m_i];
@@ -221,6 +231,25 @@ namespace KeePassLib.Cryptography
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
public static string Benchmark()
{
@@ -237,21 +266,20 @@ namespace KeePassLib.Cryptography
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 };
int nStart = Environment.TickCount;
int tStart = Environment.TickCount;
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
}

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/*
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
it under the terms of the GNU General Public License as published by
@@ -19,122 +19,81 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Text;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.PasswordGenerator
{
public sealed class PwCharSet
public sealed class PwCharSet : IEquatable<PwCharSet>
{
public const string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public const string LowerCase = "abcdefghijklmnopqrstuvwxyz";
public const string Digits = "0123456789";
public static readonly string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static readonly string LowerCase = "abcdefghijklmnopqrstuvwxyz";
public static readonly string Digits = "0123456789";
public const string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ";
public const string LowerConsonants = "bcdfghjklmnpqrstvwxyz";
public const string UpperVowels = "AEIOU";
public const string LowerVowels = "aeiou";
public static readonly string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ";
public static readonly string LowerConsonants = "bcdfghjklmnpqrstvwxyz";
public static readonly string UpperVowels = "AEIOU";
public static readonly string LowerVowels = "aeiou";
public const string Punctuation = @",.;:";
public const string Brackets = @"[]{}()<>";
public static readonly string Punctuation = ",.;:";
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 const string LowerHex = "0123456789abcdef";
public static readonly string UpperHex = "0123456789ABCDEF";
public static readonly string LowerHex = "0123456789abcdef";
public const string Invalid = "\t\r\n";
public const string LookAlike = @"O0l1I|";
internal const string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits;
private const int CharTabSize = (0x10000 / 8);
private List<char> m_vChars = new List<char>();
private byte[] m_vTab = new byte[CharTabSize];
private static string m_strHighAnsi = null;
public static string HighAnsiChars
{
get
{
if(m_strHighAnsi == null) { new PwCharSet(); } // Create string
Debug.Assert(m_strHighAnsi != null);
return m_strHighAnsi;
}
}
private static string m_strSpecial = null;
public static string SpecialChars
{
get
{
if(m_strSpecial == null) { new PwCharSet(); } // Create string
Debug.Assert(m_strSpecial != null);
return m_strSpecial;
}
}
public static readonly string LookAlike = "O0Il1|";
/// <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>
public PwCharSet()
{
Initialize(true);
Debug.Assert(PwCharSet.Latin1S.Length == (16 * 6 - 2));
}
public PwCharSet(string strCharSet)
{
Initialize(true);
Add(strCharSet);
}
private PwCharSet(bool bFullInitialize)
{
Initialize(bFullInitialize);
}
private void Initialize(bool bFullInitialize)
{
Clear();
if(!bFullInitialize) return;
if(m_strHighAnsi == null)
{
StringBuilder sbHighAnsi = new StringBuilder();
// [U+0080, U+009F] are C1 control characters,
// U+00A0 is non-breaking space
for(char ch = '\u00A1'; ch <= '\u00AC'; ++ch)
sbHighAnsi.Append(ch);
// U+00AD is soft hyphen (format character)
for(char ch = '\u00AE'; ch < '\u00FF'; ++ch)
sbHighAnsi.Append(ch);
sbHighAnsi.Append('\u00FF');
m_strHighAnsi = sbHighAnsi.ToString();
}
if(m_strSpecial == null)
{
PwCharSet pcs = new PwCharSet(false);
pcs.AddRange('!', '/');
pcs.AddRange(':', '@');
pcs.AddRange('[', '`');
pcs.Add(@"|~");
pcs.Remove(@"-_ ");
pcs.Remove(PwCharSet.Brackets);
m_strSpecial = pcs.ToString();
}
}
/// <summary>
/// Number of characters in this set.
/// </summary>
public uint Size
{
get { return (uint)m_vChars.Count; }
get { return (uint)m_lChars.Count; }
}
/// <summary>
@@ -147,19 +106,39 @@ namespace KeePassLib.Cryptography.PasswordGenerator
{
get
{
if(uPos >= (uint)m_vChars.Count)
if (uPos >= (uint)m_lChars.Count)
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>
/// Remove all characters from this set.
/// </summary>
public void Clear()
{
m_vChars.Clear();
m_lChars.Clear();
Array.Clear(m_vTab, 0, m_vTab.Length);
}
@@ -171,11 +150,11 @@ namespace KeePassLib.Cryptography.PasswordGenerator
public bool Contains(string strCharacters)
{
Debug.Assert(strCharacters != null);
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
if (strCharacters == null) throw new ArgumentNullException("strCharacters");
foreach(char ch in strCharacters)
foreach (char ch in strCharacters)
{
if(!Contains(ch)) return false;
if (!Contains(ch)) return false;
}
return true;
@@ -187,11 +166,11 @@ namespace KeePassLib.Cryptography.PasswordGenerator
/// <param name="ch">Character to add.</param>
public void Add(char ch)
{
if(ch == char.MinValue) { Debug.Assert(false); return; }
if (ch == char.MinValue) { Debug.Assert(false); return; }
if(!Contains(ch))
if (!Contains(ch))
{
m_vChars.Add(ch);
m_lChars.Add(ch);
m_vTab[ch / 8] |= (byte)(1 << (ch % 8));
}
}
@@ -203,11 +182,9 @@ namespace KeePassLib.Cryptography.PasswordGenerator
public void Add(string strCharSet)
{
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);
}
@@ -226,9 +203,7 @@ namespace KeePassLib.Cryptography.PasswordGenerator
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(chMax);
@@ -238,14 +213,16 @@ namespace KeePassLib.Cryptography.PasswordGenerator
{
bool bResult = true;
switch(chCharSetIdentifier)
switch (chCharSetIdentifier)
{
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;
case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break;
case 'c': Add(PwCharSet.LowerConsonants); break;
case 'C': Add(PwCharSet.LowerConsonants,
case 'C':
Add(PwCharSet.LowerConsonants,
PwCharSet.UpperConsonants); break;
case 'z': Add(PwCharSet.UpperConsonants); break;
case 'd': Add(PwCharSet.Digits); break; // Digit
@@ -257,12 +234,13 @@ namespace KeePassLib.Cryptography.PasswordGenerator
case 'p': Add(PwCharSet.Punctuation); break;
case 'b': Add(PwCharSet.Brackets); break;
case 's': Add(PwCharSet.PrintableAsciiSpecial); break;
case 'S': Add(PwCharSet.UpperCase, PwCharSet.LowerCase);
case 'S':
Add(PwCharSet.UpperCase, PwCharSet.LowerCase);
Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break;
case 'v': Add(PwCharSet.LowerVowels); break;
case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break;
case 'Z': Add(PwCharSet.UpperVowels); break;
case 'x': Add(m_strHighAnsi); break;
case 'x': Add(PwCharSet.Latin1S); break;
default: bResult = false; break;
}
@@ -272,18 +250,18 @@ namespace KeePassLib.Cryptography.PasswordGenerator
public bool Remove(char ch)
{
m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8)));
return m_vChars.Remove(ch);
return m_lChars.Remove(ch);
}
public bool Remove(string strCharacters)
{
Debug.Assert(strCharacters != null);
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
if (strCharacters == null) throw new ArgumentNullException("strCharacters");
bool bResult = true;
foreach(char ch in strCharacters)
foreach (char ch in strCharacters)
{
if(!Remove(ch)) bResult = false;
if (!Remove(ch)) bResult = false;
}
return bResult;
@@ -292,9 +270,9 @@ namespace KeePassLib.Cryptography.PasswordGenerator
public bool RemoveIfAllExist(string strCharacters)
{
Debug.Assert(strCharacters != null);
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
if (strCharacters == null) throw new ArgumentNullException("strCharacters");
if(!Contains(strCharacters))
if (!Contains(strCharacters))
return false;
return Remove(strCharacters);
@@ -306,8 +284,8 @@ namespace KeePassLib.Cryptography.PasswordGenerator
/// <returns>String containing all character set characters.</returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach(char ch in m_vChars)
StringBuilder sb = new StringBuilder(m_lChars.Count);
foreach (char ch in m_lChars)
sb.Append(ch);
return sb.ToString();
@@ -320,32 +298,32 @@ namespace KeePassLib.Cryptography.PasswordGenerator
sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_');
sb.Append(RemoveIfAllExist(m_strSpecial) ? 'S' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Special) ? 'S' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_');
sb.Append(RemoveIfAllExist(@"-") ? 'm' : '_');
sb.Append(RemoveIfAllExist(@"_") ? 'u' : '_');
sb.Append(RemoveIfAllExist(@" ") ? 's' : '_');
sb.Append(RemoveIfAllExist("-") ? 'm' : '_');
sb.Append(RemoveIfAllExist("_") ? 'u' : '_');
sb.Append(RemoveIfAllExist(" ") ? 's' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_');
sb.Append(RemoveIfAllExist(m_strHighAnsi) ? 'H' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Latin1S) ? 'H' : '_');
return sb.ToString();
}
public void UnpackCharRanges(string strRanges)
{
if(strRanges == null) { Debug.Assert(false); return; }
if(strRanges.Length < 10) { Debug.Assert(false); return; }
if (strRanges == null) { Debug.Assert(false); return; }
if (strRanges.Length < 10) { Debug.Assert(false); return; }
if(strRanges[0] != '_') Add(PwCharSet.UpperCase);
if(strRanges[1] != '_') Add(PwCharSet.LowerCase);
if(strRanges[2] != '_') Add(PwCharSet.Digits);
if(strRanges[3] != '_') Add(m_strSpecial);
if(strRanges[4] != '_') Add(PwCharSet.Punctuation);
if(strRanges[5] != '_') Add('-');
if(strRanges[6] != '_') Add('_');
if(strRanges[7] != '_') Add(' ');
if(strRanges[8] != '_') Add(PwCharSet.Brackets);
if(strRanges[9] != '_') Add(m_strHighAnsi);
if (strRanges[0] != '_') Add(PwCharSet.UpperCase);
if (strRanges[1] != '_') Add(PwCharSet.LowerCase);
if (strRanges[2] != '_') Add(PwCharSet.Digits);
if (strRanges[3] != '_') Add(PwCharSet.Special);
if (strRanges[4] != '_') Add(PwCharSet.Punctuation);
if (strRanges[5] != '_') Add('-');
if (strRanges[6] != '_') Add('_');
if (strRanges[7] != '_') Add(' ');
if (strRanges[8] != '_') Add(PwCharSet.Brackets);
if (strRanges[9] != '_') Add(PwCharSet.Latin1S);
}
}
}

View File

@@ -1,6 +1,6 @@
/*
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
it under the terms of the GNU General Public License as published by
@@ -20,9 +20,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
@@ -33,95 +37,78 @@ namespace KeePassLib.Cryptography.PasswordGenerator
Success = 0,
Unknown = 1,
TooFewCharacters = 2,
UnknownAlgorithm = 3
UnknownAlgorithm = 3,
InvalidCharSet = 4,
InvalidPattern = 5
}
/// <summary>
/// Utility functions for generating random passwords.
/// Password generator.
/// </summary>
public static class PwGenerator
{
public static PwgError Generate(out ProtectedString psOut,
PwProfile pwProfile, byte[] pbUserEntropy,
CustomPwGeneratorPool pwAlgorithmPool)
private static CryptoRandomStream CreateRandomStream(byte[] pbAdditionalEntropy,
out byte[] pbKey)
{
Debug.Assert(pwProfile != null);
if (pwProfile == null) throw new ArgumentNullException("pwProfile");
CryptoRandomStream crs = CreateCryptoStream(pbUserEntropy);
PwgError e = PwgError.Unknown;
if (pwProfile.GeneratorType == PasswordGeneratorType.CharSet)
e = CharSetBasedGenerator.Generate(out psOut, pwProfile, crs);
else if (pwProfile.GeneratorType == PasswordGeneratorType.Pattern)
e = PatternBasedGenerator.Generate(out psOut, pwProfile, crs);
else if (pwProfile.GeneratorType == PasswordGeneratorType.Custom)
e = GenerateCustom(out psOut, pwProfile, crs, pwAlgorithmPool);
else { Debug.Assert(false); psOut = ProtectedString.Empty; }
return e;
}
private static CryptoRandomStream CreateCryptoStream(byte[] pbAdditionalEntropy)
{
byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(128);
pbKey = CryptoRandom.Instance.GetRandomBytes(128);
// Mix in additional entropy
Debug.Assert(pbKey.Length >= 64);
if ((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0))
if ((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length != 0))
{
using (SHA512Managed h = new SHA512Managed())
{
byte[] pbHash = h.ComputeHash(pbAdditionalEntropy);
MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length);
MemUtil.ZeroByteArray(pbHash);
}
}
return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
}
internal static char GenerateCharacter(PwProfile pwProfile,
PwCharSet pwCharSet, CryptoRandomStream crsRandomSource)
internal static char GenerateCharacter(PwCharSet pwCharSet,
CryptoRandomStream crsRandomSource)
{
if (pwCharSet.Size == 0) return char.MinValue;
uint cc = pwCharSet.Size;
if (cc == 0) return char.MinValue;
ulong uIndex = crsRandomSource.GetRandomUInt64();
uIndex %= (ulong)pwCharSet.Size;
char ch = pwCharSet[(uint)uIndex];
if (pwProfile.NoRepeatingCharacters)
pwCharSet.Remove(ch);
return ch;
uint i = (uint)crsRandomSource.GetRandomUInt64(cc);
return pwCharSet[i];
}
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.ExcludeCharacters.Length > 0)
if (!string.IsNullOrEmpty(pwProfile.ExcludeCharacters))
pwCharSet.Remove(pwProfile.ExcludeCharacters);
return true;
}
internal static void ShufflePassword(char[] pPassword,
CryptoRandomStream crsRandomSource)
internal static void Shuffle(char[] v, CryptoRandomStream crsRandomSource)
{
Debug.Assert(pPassword != null); if (pPassword == null) return;
Debug.Assert(crsRandomSource != null); if (crsRandomSource == null) return;
if (v == null) { Debug.Assert(false); return; }
if (crsRandomSource == null) { Debug.Assert(false); return; }
if (pPassword.Length <= 1) return; // Nothing to shuffle
for (int nSelect = 0; nSelect < pPassword.Length; ++nSelect)
for (int i = v.Length - 1; i >= 1; --i)
{
ulong uRandomIndex = crsRandomSource.GetRandomUInt64();
uRandomIndex %= (ulong)(pPassword.Length - nSelect);
int j = (int)crsRandomSource.GetRandomUInt64((ulong)(i + 1));
char chTemp = pPassword[nSelect];
pPassword[nSelect] = pPassword[nSelect + (int)uRandomIndex];
pPassword[nSelect + (int)uRandomIndex] = chTemp;
char t = v[i];
v[i] = v[j];
v[j] = t;
}
}
@@ -135,7 +122,7 @@ namespace KeePassLib.Cryptography.PasswordGenerator
if (pwAlgorithmPool == null) return PwgError.UnknownAlgorithm;
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);
PwUuid uuid = new PwUuid(pbUuid);
@@ -148,5 +135,57 @@ namespace KeePassLib.Cryptography.PasswordGenerator
psOut = pwd;
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;
}
}
}

View File

@@ -1,6 +1,6 @@
/*
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
it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Text;
using KeePassLib.Utility;
@@ -28,17 +28,19 @@ namespace KeePassLib.Cryptography
{
public static class PopularPasswords
{
private static Dictionary<int, Dictionary<string, bool>> m_dicts =
new Dictionary<int, Dictionary<string, bool>>();
private static readonly Dictionary<int, Dictionary<char[], bool>> g_dicts =
new Dictionary<int, Dictionary<char[], bool>>();
internal static int MaxLength
{
get
{
Debug.Assert(g_dicts.Count > 0); // Should be initialized
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;
}
return iMaxLen;
@@ -47,8 +49,8 @@ namespace KeePassLib.Cryptography
internal static bool ContainsLength(int nLength)
{
Dictionary<string, bool> dDummy;
return m_dicts.TryGetValue(nLength, out dDummy);
Dictionary<char[], bool> dDummy;
return g_dicts.TryGetValue(nLength, out dDummy);
}
public static bool IsPopularPassword(char[] vPassword)
@@ -59,74 +61,73 @@ namespace KeePassLib.Cryptography
public static bool IsPopularPassword(char[] vPassword, out ulong uDictSize)
{
if(vPassword == null) throw new ArgumentNullException("vPassword");
if(vPassword.Length == 0) { uDictSize = 0; return false; }
if (vPassword == null) throw new ArgumentNullException("vPassword");
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); }
catch(Exception) { Debug.Assert(false); }
try { return IsPopularPasswordPriv(vPassword, out uDictSize); }
catch (Exception) { Debug.Assert(false); }
uDictSize = 0;
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;
if(!m_dicts.TryGetValue(str.Length, out d))
Dictionary<char[], bool> d;
if (!g_dicts.TryGetValue(vPassword.Length, out d))
{
uDictSize = 0;
return false;
}
uDictSize = (ulong)d.Count;
return d.ContainsKey(str);
return d.ContainsKey(vPassword);
}
public static void Add(byte[] pbData, bool bGZipped)
{
try
{
if(bGZipped)
if (bGZipped)
pbData = MemUtil.Decompress(pbData);
string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length);
if(string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; }
if(!char.IsWhiteSpace(strData[strData.Length - 1]))
strData += "\n";
if (string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; }
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;
if(cc > 0)
if (cc > 0)
{
string strWord = sb.ToString();
Debug.Assert(strWord.Length == cc);
char[] vWord = new char[cc];
sb.CopyTo(0, vWord, 0, cc);
Dictionary<string, bool> d;
if(!m_dicts.TryGetValue(cc, out d))
Dictionary<char[], bool> d;
if (!g_dicts.TryGetValue(cc, out d))
{
d = new Dictionary<string, bool>();
m_dicts[cc] = d;
d = new Dictionary<char[], bool>(MemUtil.ArrayHelperExOfChar);
g_dicts[cc] = d;
}
d[strWord] = true;
d[vWord] = true;
sb.Remove(0, cc);
}
}
else sb.Append(char.ToLower(ch));
}
}
catch(Exception) { Debug.Assert(false); }
catch (Exception) { Debug.Assert(false); }
}
}
}

View File

@@ -1,6 +1,6 @@
/*
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
it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Text;
using KeePassLib.Cryptography.PasswordGenerator;
using KeePassLib.Utility;
@@ -35,19 +35,19 @@ namespace KeePassLib.Cryptography
{
private static class PatternID
{
public const char LowerAlpha = 'L';
public const char UpperAlpha = 'U';
public const char Digit = 'D';
public const char Special = 'S';
public const char High = 'H';
public const char Other = 'X';
internal const char LowerAlpha = 'L';
internal const char UpperAlpha = 'U';
internal const char Digit = 'D';
internal const char Special = 'S';
internal const char Latin1S = 'H';
internal const char Other = 'X';
public const char Dictionary = 'W';
public const char Repetition = 'R';
public const char Number = 'N';
public const char DiffSeq = 'C';
internal const char Dictionary = 'W';
internal const char Repetition = 'R';
internal const char Number = 'N';
internal const char DiffSeq = 'C';
public const string All = "LUDSHXWRNC";
internal const string All = "LUDSHXWRNC";
}
// private static class CharDistrib
@@ -84,8 +84,8 @@ namespace KeePassLib.Cryptography
public QeCharType(char chTypeID, string strAlphabet, bool bIsConsecutive)
{
if(strAlphabet == null) throw new ArgumentNullException();
if(strAlphabet.Length == 0) throw new ArgumentException();
if (strAlphabet == null) throw new ArgumentNullException();
if (strAlphabet.Length == 0) throw new ArgumentException();
m_chTypeID = chTypeID;
m_strAlph = strAlphabet;
@@ -101,7 +101,7 @@ namespace KeePassLib.Cryptography
public QeCharType(char chTypeID, int nChars) // Catch-none set
{
if(nChars <= 0) throw new ArgumentOutOfRangeException();
if (nChars <= 0) throw new ArgumentOutOfRangeException();
m_chTypeID = chTypeID;
m_strAlph = string.Empty;
@@ -114,7 +114,7 @@ namespace KeePassLib.Cryptography
public bool Contains(char ch)
{
if(m_chLast != char.MinValue)
if (m_chLast != char.MinValue)
return ((ch >= m_chFirst) && (ch <= m_chLast));
Debug.Assert(m_strAlph.Length > 0); // Don't call for catch-none set
@@ -125,7 +125,7 @@ namespace KeePassLib.Cryptography
private sealed class EntropyEncoder
{
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_uCharWeight;
private readonly ulong m_uOccExclThreshold;
@@ -133,8 +133,8 @@ namespace KeePassLib.Cryptography
public EntropyEncoder(string strAlphabet, ulong uBaseWeight,
ulong uCharWeight, ulong uOccExclThreshold)
{
if(strAlphabet == null) throw new ArgumentNullException();
if(strAlphabet.Length == 0) throw new ArgumentException();
if (strAlphabet == null) throw new ArgumentNullException();
if (strAlphabet.Length == 0) throw new ArgumentException();
m_strAlph = strAlphabet;
m_uBaseWeight = uBaseWeight;
@@ -143,7 +143,7 @@ namespace KeePassLib.Cryptography
#if DEBUG
Dictionary<char, bool> d = new Dictionary<char, bool>();
foreach(char ch in m_strAlph) { d[ch] = true; }
foreach (char ch in m_strAlph) { d[ch] = true; }
Debug.Assert(d.Count == m_strAlph.Length); // No duplicates
#endif
}
@@ -166,18 +166,18 @@ namespace KeePassLib.Cryptography
public double GetOutputSize()
{
ulong uTotalWeight = m_uBaseWeight * (ulong)m_strAlph.Length;
foreach(ulong u in m_dHisto.Values)
foreach (ulong u in m_dHisto.Values)
{
Debug.Assert(u >= 1);
if(u > m_uOccExclThreshold)
if (u > m_uOccExclThreshold)
uTotalWeight += (u - m_uOccExclThreshold) * m_uCharWeight;
}
double dSize = 0.0, dTotalWeight = (double)uTotalWeight;
foreach(ulong u in m_dHisto.Values)
foreach (ulong u in m_dHisto.Values)
{
ulong uWeight = m_uBaseWeight;
if(u > m_uOccExclThreshold)
if (u > m_uOccExclThreshold)
uWeight += (u - m_uOccExclThreshold) * m_uCharWeight;
dSize -= (double)u * Log2((double)uWeight / dTotalWeight);
@@ -189,7 +189,7 @@ namespace KeePassLib.Cryptography
private sealed class MultiEntropyEncoder
{
private Dictionary<char, EntropyEncoder> m_dEncs =
private readonly Dictionary<char, EntropyEncoder> m_dEncs =
new Dictionary<char, EntropyEncoder>();
public MultiEntropyEncoder()
@@ -198,7 +198,7 @@ namespace KeePassLib.Cryptography
public void AddEncoder(char chTypeID, EntropyEncoder ec)
{
if(ec == null) { Debug.Assert(false); return; }
if (ec == null) { Debug.Assert(false); return; }
Debug.Assert(!m_dEncs.ContainsKey(chTypeID));
m_dEncs[chTypeID] = ec;
@@ -206,13 +206,13 @@ namespace KeePassLib.Cryptography
public void Reset()
{
foreach(EntropyEncoder ec in m_dEncs.Values) { ec.Reset(); }
foreach (EntropyEncoder ec in m_dEncs.Values) { ec.Reset(); }
}
public bool Write(char chTypeID, char chData)
{
EntropyEncoder ec;
if(!m_dEncs.TryGetValue(chTypeID, out ec))
if (!m_dEncs.TryGetValue(chTypeID, out ec))
return false;
ec.Write(chData);
@@ -223,7 +223,7 @@ namespace KeePassLib.Cryptography
{
double d = 0.0;
foreach(EntropyEncoder ec in m_dEncs.Values)
foreach (EntropyEncoder ec in m_dEncs.Values)
{
d += ec.GetOutputSize();
}
@@ -281,36 +281,31 @@ 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 void EnsureInitialized()
{
lock(m_objSyncInit)
lock (m_objSyncInit)
{
if(m_lCharTypes == null)
if (m_lCharTypes == null)
{
string strSpecial = PwCharSet.PrintableAsciiSpecial;
if(strSpecial.IndexOf(' ') >= 0) { Debug.Assert(false); }
else strSpecial = strSpecial + " ";
if (strSpecial.IndexOf(' ') >= 0) { Debug.Assert(false); }
else strSpecial += " ";
int nSp = strSpecial.Length;
int nHi = PwCharSet.HighAnsiChars.Length;
int nL1S = PwCharSet.Latin1S.Length;
m_lCharTypes = new List<QeCharType>();
m_lCharTypes.Add(new QeCharType(PatternID.LowerAlpha,
PwCharSet.LowerCase, true));
m_lCharTypes.Add(new QeCharType(PatternID.UpperAlpha,
PwCharSet.UpperCase, true));
m_lCharTypes.Add(new QeCharType(PatternID.Digit,
PwCharSet.Digits, true));
m_lCharTypes.Add(new QeCharType(PatternID.Special,
strSpecial, false));
m_lCharTypes.Add(new QeCharType(PatternID.High,
PwCharSet.HighAnsiChars, false));
m_lCharTypes.Add(new QeCharType(PatternID.Other,
0x10000 - (2 * 26) - 10 - nSp - nHi));
m_lCharTypes = new List<QeCharType>()
{
new QeCharType(PatternID.LowerAlpha, PwCharSet.LowerCase, true),
new QeCharType(PatternID.UpperAlpha, PwCharSet.UpperCase, true),
new QeCharType(PatternID.Digit, PwCharSet.Digits, true),
new QeCharType(PatternID.Special, strSpecial, false),
new QeCharType(PatternID.Latin1S, PwCharSet.Latin1S, false),
new QeCharType(PatternID.Other, 0x10000 - (2 * 26) - 10 - nSp - nL1S)
};
}
}
}
@@ -318,37 +313,37 @@ namespace KeePassLib.Cryptography
/// <summary>
/// Estimate the quality of a password.
/// </summary>
/// <param name="vPasswordChars">Password to check.</param>
/// <param name="vPassword">Password to check.</param>
/// <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(vPasswordChars.Length == 0) return 0;
if (vPassword == null) { Debug.Assert(false); return 0; }
if (vPassword.Length == 0) return 0;
EnsureInitialized();
int n = vPasswordChars.Length;
int n = vPassword.Length;
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>();
QePatternInstance piChar = new QePatternInstance(i, 1,
GetCharType(vPasswordChars[i]));
GetCharType(vPassword[i]));
vPatterns[i].Add(piChar);
}
FindRepetitions(vPasswordChars, vPatterns);
FindNumbers(vPasswordChars, vPatterns);
FindDiffSeqs(vPasswordChars, vPatterns);
FindPopularPasswords(vPasswordChars, vPatterns);
FindRepetitions(vPassword, vPatterns);
FindNumbers(vPassword, vPatterns);
FindDiffSeqs(vPassword, vPatterns);
FindPopularPasswords(vPassword, vPatterns);
// Encoders must not be static, because the entropy estimation
// may run concurrently in multiple threads and the encoders are
// not read-only
EntropyEncoder ecPattern = new EntropyEncoder(PatternID.All, 0, 1, 0);
MultiEntropyEncoder mcData = new MultiEntropyEncoder();
for(int i = 0; i < (m_lCharTypes.Count - 1); ++i)
for (int i = 0; i < (m_lCharTypes.Count - 1); ++i)
{
// Let m be the alphabet size. In order to ensure that two same
// characters cost at least as much as a single character, for
@@ -371,25 +366,25 @@ namespace KeePassLib.Cryptography
Stack<QePathState> sRec = new Stack<QePathState>();
sRec.Push(new QePathState(0, new List<QePatternInstance>()));
while(sRec.Count > 0)
while (sRec.Count > 0)
{
int tDiff = Environment.TickCount - tStart;
if(tDiff > 500) break;
if (tDiff > 500) break;
QePathState s = sRec.Pop();
if(s.Position >= n)
if (s.Position >= n)
{
Debug.Assert(s.Position == n);
double dblCost = ComputePathCost(s.Path, vPasswordChars,
double dblCost = ComputePathCost(s.Path, vPassword,
ecPattern, mcData);
if(dblCost < dblMinCost) dblMinCost = dblCost;
if (dblCost < dblMinCost) dblMinCost = dblCost;
}
else
{
List<QePatternInstance> lSubs = vPatterns[s.Position];
for(int i = lSubs.Count - 1; i >= 0; --i)
for (int i = lSubs.Count - 1; i >= 0; --i)
{
QePatternInstance pi = lSubs[i];
Debug.Assert(pi.Position == s.Position);
@@ -418,13 +413,14 @@ namespace KeePassLib.Cryptography
/// <returns>Estimated bit-strength of the password.</returns>
public static uint EstimatePasswordBits(byte[] pbUnprotectedUtf8)
{
if(pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; }
if (pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; }
char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
uint uResult = EstimatePasswordBits(vChars);
MemUtil.ZeroArray<char>(vChars);
char[] v = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
uint r;
try { r = EstimatePasswordBits(v); }
finally { MemUtil.ZeroArray<char>(v); }
return uResult;
return r;
}
private static QeCharType GetCharType(char ch)
@@ -432,9 +428,9 @@ namespace KeePassLib.Cryptography
int nTypes = m_lCharTypes.Count;
Debug.Assert((nTypes > 0) && (m_lCharTypes[nTypes - 1].CharCount > 256));
for(int i = 0; i < (nTypes - 1); ++i)
for (int i = 0; i < (nTypes - 1); ++i)
{
if(m_lCharTypes[i].Contains(ch))
if (m_lCharTypes[i].Contains(ch))
return m_lCharTypes[i];
}
@@ -445,19 +441,19 @@ namespace KeePassLib.Cryptography
char[] vPassword, EntropyEncoder ecPattern, MultiEntropyEncoder mcData)
{
ecPattern.Reset();
for(int i = 0; i < l.Count; ++i)
for (int i = 0; i < l.Count; ++i)
ecPattern.Write(l[i].PatternID);
double dblPatternCost = ecPattern.GetOutputSize();
mcData.Reset();
double dblDataCost = 0.0;
foreach(QePatternInstance pi in l)
foreach (QePatternInstance pi in l)
{
QeCharType tChar = pi.SingleCharType;
if(tChar != null)
if (tChar != null)
{
char ch = vPassword[pi.Position];
if(!mcData.Write(tChar.TypeID, ch))
if (!mcData.Write(tChar.TypeID, ch))
dblDataCost += pi.Cost;
}
else dblDataCost += pi.Cost;
@@ -474,7 +470,7 @@ namespace KeePassLib.Cryptography
char[] vLower = new char[n];
char[] vLeet = new char[n];
for(int i = 0; i < n; ++i)
for (int i = 0; i < n; ++i)
{
char ch = vPassword[i];
@@ -482,27 +478,27 @@ namespace KeePassLib.Cryptography
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);
int nMaxLen = Math.Min(n, PopularPasswords.MaxLength);
for(int nSubLen = nMaxLen; nSubLen >= 3; --nSubLen)
for (int nSubLen = nMaxLen; nSubLen >= 3; --nSubLen)
{
if(!PopularPasswords.ContainsLength(nSubLen)) continue;
if (!PopularPasswords.ContainsLength(nSubLen)) continue;
char[] vSub = new char[nSubLen];
for(int i = 0; i <= (n - nSubLen); ++i)
for (int i = 0; i <= (n - nSubLen); ++i)
{
if(Array.IndexOf<char>(vLower, chErased, i, nSubLen) >= 0)
if (Array.IndexOf<char>(vLower, chErased, i, nSubLen) >= 0)
continue;
Array.Copy(vLower, i, vSub, 0, nSubLen);
if(!EvalAddPopularPasswordPattern(vPatterns, vPassword,
if (!EvalAddPopularPasswordPattern(vPatterns, vPassword,
i, vSub, 0.0))
{
Array.Copy(vLeet, i, vSub, 0, nSubLen);
if(EvalAddPopularPasswordPattern(vPatterns, vPassword,
if (EvalAddPopularPasswordPattern(vPatterns, vPassword,
i, vSub, 1.5))
{
Array.Clear(vLower, i, nSubLen); // Not vLeet
@@ -515,14 +511,19 @@ namespace KeePassLib.Cryptography
Debug.Assert(vLower[i] == chErased);
}
}
MemUtil.ZeroArray<char>(vSub);
}
MemUtil.ZeroArray<char>(vLower);
MemUtil.ZeroArray<char>(vLeet);
}
private static bool EvalAddPopularPasswordPattern(List<QePatternInstance>[] vPatterns,
char[] vPassword, int i, char[] vSub, double dblCostPerMod)
{
ulong uDictSize;
if(!PopularPasswords.IsPopularPassword(vSub, out uDictSize))
if (!PopularPasswords.IsPopularPassword(vSub, out uDictSize))
return false;
int n = vSub.Length;
@@ -532,9 +533,9 @@ namespace KeePassLib.Cryptography
// dblCost += log2(n binom d)
int k = Math.Min(d, n - d);
for(int j = n; j > (n - k); --j)
for (int j = n; j > (n - k); --j)
dblCost += Log2(j);
for(int j = k; j >= 2; --j)
for (int j = k; j >= 2; --j)
dblCost -= Log2(j);
dblCost += dblCostPerMod * (double)d;
@@ -546,19 +547,19 @@ namespace KeePassLib.Cryptography
private static char DecodeLeetChar(char chLeet)
{
if((chLeet >= '\u00C0') && (chLeet <= '\u00C6')) return 'a';
if((chLeet >= '\u00C8') && (chLeet <= '\u00CB')) return 'e';
if((chLeet >= '\u00CC') && (chLeet <= '\u00CF')) return 'i';
if((chLeet >= '\u00D2') && (chLeet <= '\u00D6')) return 'o';
if((chLeet >= '\u00D9') && (chLeet <= '\u00DC')) return 'u';
if((chLeet >= '\u00E0') && (chLeet <= '\u00E6')) return 'a';
if((chLeet >= '\u00E8') && (chLeet <= '\u00EB')) return 'e';
if((chLeet >= '\u00EC') && (chLeet <= '\u00EF')) return 'i';
if((chLeet >= '\u00F2') && (chLeet <= '\u00F6')) return 'o';
if((chLeet >= '\u00F9') && (chLeet <= '\u00FC')) return 'u';
if ((chLeet >= '\u00C0') && (chLeet <= '\u00C6')) return 'a';
if ((chLeet >= '\u00C8') && (chLeet <= '\u00CB')) return 'e';
if ((chLeet >= '\u00CC') && (chLeet <= '\u00CF')) return 'i';
if ((chLeet >= '\u00D2') && (chLeet <= '\u00D6')) return 'o';
if ((chLeet >= '\u00D9') && (chLeet <= '\u00DC')) return 'u';
if ((chLeet >= '\u00E0') && (chLeet <= '\u00E6')) return 'a';
if ((chLeet >= '\u00E8') && (chLeet <= '\u00EB')) return 'e';
if ((chLeet >= '\u00EC') && (chLeet <= '\u00EF')) return 'i';
if ((chLeet >= '\u00F2') && (chLeet <= '\u00F6')) return 'o';
if ((chLeet >= '\u00F9') && (chLeet <= '\u00FC')) return 'u';
char ch;
switch(chLeet)
switch (chLeet)
{
case '4':
case '@':
@@ -621,9 +622,9 @@ namespace KeePassLib.Cryptography
char[] v2, int iOffset2, int nLength)
{
int nDist = 0;
for(int i = 0; i < nLength; ++i)
for (int i = 0; i < nLength; ++i)
{
if(v1[iOffset1 + i] != v2[iOffset2 + i]) ++nDist;
if (v1[iOffset1 + i] != v2[iOffset2 + i]) ++nDist;
}
return nDist;
@@ -637,15 +638,15 @@ namespace KeePassLib.Cryptography
Array.Copy(vPassword, v, n);
char chErased = char.MaxValue;
for(int m = (n / 2); m >= 3; --m)
for (int m = (n / 2); m >= 3; --m)
{
for(int x1 = 0; x1 <= (n - (2 * m)); ++x1)
for (int x1 = 0; x1 <= (n - (2 * m)); ++x1)
{
bool bFoundRep = false;
for(int x2 = (x1 + m); x2 <= (n - m); ++x2)
for (int x2 = (x1 + m); x2 <= (n - m); ++x2)
{
if(PartsEqual(v, x1, x2, m))
if (PartsEqual(v, x1, x2, m))
{
double dblCost = Log2(x1 + 1) + Log2(m);
vPatterns[x2].Add(new QePatternInstance(x2, m,
@@ -656,16 +657,18 @@ 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)
{
for(int i = 0; i < nLength; ++i)
for (int i = 0; i < nLength; ++i)
{
if(v[x1 + i] != v[x2 + i]) return false;
if (v[x1 + i] != v[x2 + i]) return false;
}
return true;
@@ -673,7 +676,7 @@ namespace KeePassLib.Cryptography
private static void ErasePart(char[] v, int i, int n, ref char chErased)
{
for(int j = 0; j < n; ++j)
for (int j = 0; j < n; ++j)
{
v[i + j] = chErased;
--chErased;
@@ -685,33 +688,35 @@ namespace KeePassLib.Cryptography
{
int n = vPassword.Length;
StringBuilder sb = new StringBuilder();
for(int i = 0; i < n; ++i)
for (int i = 0; i < n; ++i)
{
char ch = vPassword[i];
if((ch >= '0') && (ch <= '9')) sb.Append(ch);
if ((ch >= '0') && (ch <= '9')) sb.Append(ch);
else
{
AddNumberPattern(vPatterns, sb.ToString(), i - sb.Length);
AddNumberPattern(vPatterns, sb, i - 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,
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;
for(int j = 0; j < strNumber.Length; ++j)
for (int j = 0; j < strNumber.Length; ++j)
{
if(strNumber[j] != '0') break;
if (strNumber[j] != '0') break;
++nZeros;
}
double dblCost = Log2(nZeros + 1);
if(nZeros < strNumber.Length)
if (nZeros < strNumber.Length)
{
string strNonZero = strNumber.Substring(nZeros);
@@ -720,7 +725,7 @@ namespace KeePassLib.Cryptography
catch(Exception) { Debug.Assert(false); return; }
#else
double d;
if(double.TryParse(strNonZero, out d))
if (double.TryParse(strNonZero, out d))
dblCost += Log2(d);
else { Debug.Assert(false); return; }
#endif
@@ -733,17 +738,18 @@ namespace KeePassLib.Cryptography
private static void FindDiffSeqs(char[] vPassword,
List<QePatternInstance>[] vPatterns)
{
int d = int.MinValue, p = 0;
string str = new string(vPassword) + new string(char.MaxValue, 1);
int n = vPassword.Length;
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];
if(dCur != d)
int dCur = ((i == n) ? int.MinValue :
((int)vPassword[i] - (int)vPassword[i - 1]));
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);
vPatterns[p].Add(new QePatternInstance(p,

View File

@@ -46,4 +46,12 @@ namespace KeePassLib.Delegates
public delegate void VoidDelegate();
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);
}

File diff suppressed because it is too large Load Diff

View File

@@ -140,6 +140,10 @@ namespace keepass2android
#endif
int WebDavChunkedUploadSize
{
get;
}
}
}

View File

@@ -15,6 +15,8 @@ namespace keepass2android.Io
{
get { return false; }
}
static public bool IsConfigured => !string.IsNullOrEmpty(AppKey) && !string.IsNullOrEmpty(AppSecret);
}
public partial class DropboxAppFolderFileStorage: JavaFileStorage
@@ -29,6 +31,7 @@ namespace keepass2android.Io
get { return false; }
}
static public bool IsConfigured => !string.IsNullOrEmpty(AppKey) && !string.IsNullOrEmpty(AppSecret);
}
}

View File

@@ -123,7 +123,7 @@ namespace keepass2android.Io
}
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
public virtual IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
{
return new JavaFileStorageWriteTransaction(IocToPath(ioc), useFileTransaction, this);
}

View File

@@ -6,10 +6,12 @@ using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using Android.Widget;
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
using Keepass2android.Javafilestorage;
#endif
using KeePassLib.Serialization;
@@ -19,8 +21,14 @@ namespace keepass2android.Io
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
public class WebDavFileStorage: JavaFileStorage
{
public WebDavFileStorage(IKp2aApp app) : base(new Keepass2android.Javafilestorage.WebDavStorage(app.CertificateErrorHandler), app)
private readonly IKp2aApp _app;
private readonly WebDavStorage baseWebdavStorage;
public WebDavFileStorage(IKp2aApp app, int chunkSize) : base(new Keepass2android.Javafilestorage.WebDavStorage(app.CertificateErrorHandler, chunkSize), app)
{
_app = app;
baseWebdavStorage = (WebDavStorage)Jfs;
}
public override IEnumerable<string> SupportedProtocols
@@ -75,6 +83,15 @@ namespace keepass2android.Io
}
return base.IocToPath(ioc);
}
public override IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
{
baseWebdavStorage.SetUploadChunkSize(_app.WebDavChunkedUploadSize);
return base.OpenWriteTransaction(ioc, useFileTransaction);
}
}
#endif
}

View File

@@ -1,6 +1,7 @@
package keepass2android.javafilestorage;
import android.content.Context;
import java.math.BigInteger;
import android.content.Intent;
import android.net.Uri;
@@ -15,7 +16,10 @@ import com.burgstaller.okhttp.basic.BasicAuthenticator;
import com.burgstaller.okhttp.digest.CachingAuthenticator;
import com.burgstaller.okhttp.digest.DigestAuthenticator;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
@@ -24,6 +28,7 @@ import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -44,23 +49,33 @@ import keepass2android.javafilestorage.webdav.DecoratedTrustManager;
import keepass2android.javafilestorage.webdav.PropfindXmlParser;
import keepass2android.javafilestorage.webdav.WebDavUtil;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.internal.tls.OkHostnameVerifier;
import okio.BufferedSink;
public class WebDavStorage extends JavaFileStorageBase {
private final ICertificateErrorHandler mCertificateErrorHandler;
private Context appContext;
public WebDavStorage(ICertificateErrorHandler certificateErrorHandler)
int chunkSize;
public WebDavStorage(ICertificateErrorHandler certificateErrorHandler, int chunkSize)
{
this.chunkSize = chunkSize;
mCertificateErrorHandler = certificateErrorHandler;
}
public void setUploadChunkSize(int chunkSize)
{
this.chunkSize = chunkSize;
}
public String buildFullPath(String url, String username, String password) throws UnsupportedEncodingException {
String scheme = url.substring(0, url.indexOf("://"));
url = url.substring(scheme.length() + 3);
@@ -181,21 +196,119 @@ public class WebDavStorage extends JavaFileStorageBase {
return client;
}
public void renameOrMoveWebDavResource(String sourcePath, String destinationPath, boolean overwrite) throws Exception {
ConnectionInfo sourceCi = splitStringToConnectionInfo(sourcePath);
ConnectionInfo destinationCi = splitStringToConnectionInfo(destinationPath);
Request.Builder requestBuilder = new Request.Builder()
.url(new URL(sourceCi.URL))
.method("MOVE", null) // "MOVE" is the HTTP method
.header("Destination", destinationCi.URL); // New URI for the resource
// Add Overwrite header
if (overwrite) {
requestBuilder.header("Overwrite", "T"); // 'T' for true
} else {
requestBuilder.header("Overwrite", "F"); // 'F' for false
}
Request request = requestBuilder.build();
Response response = getClient(sourceCi).newCall(request).execute();
// Check the status code
if (response.isSuccessful()) {
// WebDAV MOVE can return 201 (Created) if a new resource was created at dest,
// or 204 (No Content) if moved to a pre-existing destination (e.g., just renamed).
// A 200 OK might also be returned by some servers, though 201/204 are more common.
}
else
{
throw new Exception("Rename/Move failed for " + sourceCi.URL + " to " + destinationCi.URL + ": " + response.code() + " " + response.message());
}
}
public static String generateRandomHexString(int length) {
SecureRandom secureRandom = new SecureRandom();
// Generate enough bytes to ensure we can get the desired number of hex characters.
// Each byte converts to two hex characters.
// For 8 hex characters, we need 4 bytes.
int numBytes = (int) Math.ceil(length / 2.0);
byte[] randomBytes = new byte[numBytes];
secureRandom.nextBytes(randomBytes);
// Convert the byte array to a hexadecimal string
// BigInteger(1, randomBytes) treats the byte array as a positive number.
// toString(16) converts it to a hexadecimal string.
String hexString = new BigInteger(1, randomBytes).toString(16);
// Pad with leading zeros if necessary (e.g., if the generated number is small)
// and then take the first 'length' characters.
// Using String.format to ensure leading zeros if the hexString is shorter.
return String.format("%0" + length + "d", new BigInteger(hexString, 16)).substring(0, length);
}
@Override
public void uploadFile(String path, byte[] data, boolean writeTransactional)
throws Exception {
if (writeTransactional)
{
String randomSuffix = ".tmp." + generateRandomHexString(8);
uploadFile(path + randomSuffix, data, false);
renameOrMoveWebDavResource(path+randomSuffix, path, true);
return;
}
try {
ConnectionInfo ci = splitStringToConnectionInfo(path);
RequestBody requestBody;
if (chunkSize > 0)
{
// use chunked upload
requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.parse("application/binary");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
try (InputStream in = new ByteArrayInputStream(data)) {
byte[] buffer = new byte[chunkSize];
int read;
while ((read = in.read(buffer)) != -1) {
sink.write(buffer, 0, read);
sink.flush();
}
}
}
@Override
public long contentLength() {
return -1; // use chunked upload
}
};
}
else
{
requestBody = new MultipartBody.Builder()
.addPart(RequestBody.create(data, MediaType.parse("application/binary")))
.build();
}
Request request = new Request.Builder()
.url(new URL(ci.URL))
.put(RequestBody.create(MediaType.parse("application/binary"), data))
.put(requestBody)
.build();
//TODO consider writeTransactional
//TODO check for error
Response response = getClient(ci).newCall(request).execute();
checkStatus(response);
@@ -290,7 +403,10 @@ public class WebDavStorage extends JavaFileStorageBase {
e.sizeInBytes = -1;
}
}
e.isDirectory = r.href.endsWith("/");
e.isDirectory = r.href.endsWith("/") || okprop.IsCollection;
e.displayName = okprop.DisplayName;
if (e.displayName == null)
@@ -519,3 +635,4 @@ public class WebDavStorage extends JavaFileStorageBase {
}
}

View File

@@ -57,6 +57,8 @@ public class PropfindXmlParser
public String DisplayName;
public String LastModified;
public String ContentLength;
public boolean IsCollection;
}
public String status;
public Prop prop;
@@ -191,6 +193,8 @@ public class PropfindXmlParser
continue;
}
String name = parser.getName();
String namespace = parser.getNamespace();
android.util.Log.d("PARSE", "4name = " + name);
if (name.equals("getcontentlength"))
@@ -200,6 +204,9 @@ public class PropfindXmlParser
prop.LastModified = readText(parser);
} else if (name.equals("displayname")) {
prop.DisplayName = readText(parser);
} else if (name.equals("resourcetype") && namespace.equals(ns)) {
// We found the <d:resourcetype> tag
prop.IsCollection = readResourceType(parser);
} else {
skip(parser);
}
@@ -208,6 +215,37 @@ public class PropfindXmlParser
return prop;
}
private boolean readResourceType(XmlPullParser parser) throws IOException, XmlPullParserException {
boolean isCollection = false;
parser.require(XmlPullParser.START_TAG, ns, "resourcetype");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
String namespace = parser.getNamespace();
if (name.equals("collection") && namespace.equals(ns)) {
// We found <d:collection/>, so it's a folder
isCollection = true;
// Since <d:collection/> is usually an empty tag, just consume it.
// It might contain text if there's whitespace, so consume text then end tag.
if (parser.next() == XmlPullParser.TEXT) {
parser.nextTag(); // Move to the end tag
}
parser.require(XmlPullParser.END_TAG, ns, "collection");
} else {
// Skip any other unexpected tags within <d:resourcetype>
skip(parser);
}
}
// After reading all children of <d:resourcetype>, ensure we are at its END_TAG
parser.require(XmlPullParser.END_TAG, ns, "resourcetype");
return isCollection;
}
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
android.util.Log.d("PARSE", "skipping " + parser.getName());

View File

@@ -548,7 +548,7 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
//storageToTest = new GoogleDriveAppDataFileStorage();
/*storageToTest = new WebDavStorage(new ICertificateErrorHandler() {
storageToTest = new WebDavStorage(new ICertificateErrorHandler() {
@Override
public boolean onValidationError(String error) {
return false;
@@ -558,12 +558,12 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
public boolean alwaysFailOnValidationError() {
return false;
}
});
*/
}, 64*1024);
//storageToTest = new DropboxV2Storage(ctx,"4ybka4p4a1027n6", "1z5lv528un9nre8", !simulateRestart);
//storageToTest = new DropboxFileStorage(ctx,"4ybka4p4a1027n6", "1z5lv528un9nre8", !simulateRestart);
//storageToTest = new DropboxAppFolderFileStorage(ctx,"ax0268uydp1ya57", "3s86datjhkihwyc", true);
storageToTest = new GoogleDriveFullFileStorage();
// storageToTest = new GoogleDriveFullFileStorage();
return storageToTest;

File diff suppressed because it is too large Load Diff

View File

@@ -1546,10 +1546,10 @@ namespace keepass2android
string url = _stringViews[urlFieldKey].Text;
if (url == null) return false;
// Default http:// if no protocol specified
// Default https:// if no protocol specified
if ((!url.Contains(":") || (url.StartsWith("www."))))
{
url = "http://" + url;
url = "https://" + url;
}
try

View File

@@ -9,7 +9,9 @@ using System.Text;
using Android.App;
using Android.Content;
using Android.Content.Res;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using Android.Widget;
@@ -319,7 +321,7 @@ namespace keepass2android
View dlgContents = activity.LayoutInflater.Inflate(Resource.Layout.httpcredentials, null);
if (!defaultPath.EndsWith(_schemeSeparator))
{
var webdavStorage = new Keepass2android.Javafilestorage.WebDavStorage(App.Kp2a.CertificateErrorHandler);
var webdavStorage = CreateWebdavStorage(activity);
var connInfo = webdavStorage.SplitStringToConnectionInfo(defaultPath);
dlgContents.FindViewById<EditText>(Resource.Id.http_url).Text = connInfo.Url;
dlgContents.FindViewById<EditText>(Resource.Id.http_user).Text = connInfo.Username;
@@ -339,7 +341,7 @@ namespace keepass2android
string scheme = defaultPath.Substring(0, defaultPath.IndexOf(_schemeSeparator, StringComparison.Ordinal));
if (host.Contains(_schemeSeparator) == false)
host = scheme + _schemeSeparator + host;
string httpPath = new Keepass2android.Javafilestorage.WebDavStorage(null).BuildFullPath(host, user,
string httpPath = CreateWebdavStorage(activity).BuildFullPath(host, user,
password);
onStartBrowse(httpPath);
});
@@ -352,7 +354,12 @@ namespace keepass2android
dialog.Show();
#endif
}
#if !NoNet
private static WebDavStorage CreateWebdavStorage(Activity activity)
{
return new WebDavStorage(App.Kp2a.CertificateErrorHandler, App.Kp2a.WebDavChunkedUploadSize);
}
#endif
private void ShowFtpDialog(Activity activity, Util.FileSelectedHandler onStartBrowse, Action onCancel, string defaultPath)
{
#if !NoNet
@@ -518,7 +525,7 @@ namespace keepass2android
string scheme = defaultPath.Substring(0,defaultPath.IndexOf(_schemeSeparator, StringComparison.Ordinal));
if (host.Contains(_schemeSeparator) == false)
host = scheme + _schemeSeparator + host;
string httpPath = new Keepass2android.Javafilestorage.WebDavStorage(null).BuildFullPath(WebDavFileStorage.Owncloud2Webdav(host, subtype == "owncloud" ? WebDavFileStorage.owncloudPrefix : WebDavFileStorage.nextcloudPrefix), user,
string httpPath = CreateWebdavStorage(activity).BuildFullPath(WebDavFileStorage.Owncloud2Webdav(host, subtype == "owncloud" ? WebDavFileStorage.owncloudPrefix : WebDavFileStorage.nextcloudPrefix), user,
password);
onStartBrowse(httpPath);
});

View File

@@ -23,6 +23,7 @@ using Android.App;
using Android.App.Admin;
using Android.Content;
using Android.Content.PM;
using Android.Content.Res;
using Android.Graphics;
using Android.OS;
using Android.Preferences;
@@ -66,8 +67,13 @@ namespace keepass2android
Resource.Id.cb_exclude_lookalike
};
PasswordFont _passwordFont = new PasswordFont();
private static object _popularPasswordsLock = new object();
private static bool _popularPasswordsInitialized = false;
private ActivityDesign _design;
public GeneratePasswordActivity()
@@ -302,6 +308,10 @@ namespace keepass2android
EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit);
txtPasswordToSet.TextChanged += (sender, args) =>
{
Task.Run(() => UpdatePasswordStrengthEstimate(txtPasswordToSet.Text));
};
_passwordFont.ApplyTo(txtPasswordToSet);
@@ -467,17 +477,48 @@ namespace keepass2android
return;
String password = "";
uint passwordBits = 0;
Task.Run(() =>
{
password = GeneratePassword();
passwordBits = QualityEstimation.EstimatePasswordBits(password.ToCharArray());
RunOnUiThread(() =>
{
EditText txtPassword = (EditText)FindViewById(Resource.Id.password_edit);
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);
progressBar.Progress = (int)passwordBits;
@@ -503,13 +544,7 @@ namespace keepass2android
PorterDuff.Mode.SrcIn));
FindViewById<TextView>(Resource.Id.tv_password_strength).Text = " " + passwordBits + " bits";
UpdateProfileSpinnerSelection();
});
});
}
private void UpdateProfileSpinnerSelection()

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="217"
android:versionName="1.12-r9c"
android:versionCode="221"
android:versionName="1.13-r0"
package="keepass2android.keepass2android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="217"
android:versionName="1.12-r9c"
android:versionCode="221"
android:versionName="1.13-r0"
package="keepass2android.keepass2android_nonet"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">

View File

@@ -1423,6 +1423,8 @@ namespace keepass2android
if (cbQuickUnlock == null)
throw new NullPointerException("cpQuickUnlock");
App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked);
App.Kp2a.ScreenLockWasEnabledWhenOpeningDatabase =
(((KeyguardManager)GetSystemService(Context.KeyguardService)!)!).IsDeviceSecure;
if ((_loadDbFileTask != null) && (App.Kp2a.OfflineMode != _loadDbTaskOffline))
{

View File

@@ -25,6 +25,7 @@ using Android.Widget;
using Android.Content.PM;
using KeePassLib.Keys;
using Android.Preferences;
using Android.Provider;
using Android.Runtime;
using Android.Views.InputMethods;
@@ -162,6 +163,29 @@ namespace keepass2android
if (bundle != null)
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;
}
}

View File

@@ -79,6 +79,12 @@ android:paddingRight="16dp"
android:paddingTop="16dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/QuickUnlockForm">
<TextView
android:id="@+id/QuickUnlock_label"
android:text="@string/QuickUnlock_label"
@@ -88,11 +94,6 @@ android:paddingRight="16dp"
android:textSize="14sp"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:inputType="textPassword"
android:layout_width="wrap_content"
@@ -121,6 +122,60 @@ android:paddingRight="16dp"
</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
android:id="@+id/spacing"

View File

@@ -176,7 +176,8 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="masktotp_title">TOTP-Feld verdecken</string>
<string name="masktotp_summary">TOTP-Feld standardmäßig ausblenden</string>
<string name="NoAutofillDisabling_title">Keine Option, um Autofill zu deaktivieren</string>
<string name="NoAutofillDisabling_summary">Wenn aktiviert, wird die App die Option zum Abschalten von Autofill für bestimmte Einträge nicht anzeigen. </string>
<string name="NoAutofillDisabling_summary">Wenn aktiviert, wird die App die Option zum Abschalten von Autofill für bestimmte Einträge nicht angezeigt.
</string>
<string name="menu_about">Über</string>
<string name="menu_change_key">Hauptschlüssel ändern</string>
<string name="menu_copy_pass">Passwort kopieren</string>
@@ -290,7 +291,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="please_note">Bitte beachten</string>
<string name="contributors">Mitwirkende</string>
<string name="regular_expression">Regulärer Ausdruck</string>
<string name="AlwaysMergeOnConflict_title">Bei Konflikt immer vereinigen</string>
<string name="AlwaysMergeOnConflict_title">Bei Konflikt immer zusammenführen</string>
<string name="AlwaysMergeOnConflict_summary">Die lokalen Änderungen immer mit den entfernten Änderungen vereinigen, wenn Keepass2Android erkennt, dass die entfernte Datei verändert wurde.</string>
<string name="TanExpiresOnUse_title">TAN verfällt bei Verwendung</string>
<string name="TanExpiresOnUse_summary">TAN-Einträge als abgelaufen markieren, wenn sie verwendet werden</string>
@@ -332,7 +333,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="QuickUnlockLength_summary">Maximale Anzahl von Zeichen, die als QuickUnlock-Passwort verwendet werden.</string>
<string name="QuickUnlockHideLength_title">Länge des QuickUnlock-Codes verstecken</string>
<string name="QuickUnlockHideLength_summary">Wenn aktiviert, wird die Länge des QuickUnlock-Codes nicht auf dem QuickUnlock-Bildschirm angezeigt.</string>
<string name="QuickUnlockKeyFromDatabase_title">QuickUnlock-Taste aus dem Datenbankeintrag.</string>
<string name="QuickUnlockKeyFromDatabase_title">QuickUnlock-Schlüssel aus Datenbankeintrag</string>
<string name="QuickUnlockKeyFromDatabase_summary">Wenn die aktive Datenbank einen Eintrag mit dem Titel „QuickUnlock“ in der Root-Gruppe enthält, wird das Passwort dieses Eintrags als QuickUnlock-Code verwendet.</string>
<string name="QuickUnlock_fail">QuickUnlock fehlgeschlagen: falsches Passwort!</string>
<string name="SaveAttachmentDialog_title">Anhang speichern</string>
@@ -559,7 +560,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="filestoragename_onedrive2_appfolder">Ordner der Keepass2Android-App</string>
<string name="filestoragename_sftp">SFTP (SSH File Transfer)</string>
<string name="filestoragename_mega">MEGA</string>
<string name="filestoragehelp_mega">Bitte beachte: Keepass2Android muss eineListe aller Dateien des Mega-Kontos herunterladen, um ordnungsgemäß zu funktionieren. Aus diesem Grund ist der Zugriff auf Konten mit vielen Dateien möglicherweise langsam.</string>
<string name="filestoragehelp_mega">Bitte beachte: Keepass2Android muss eine Liste aller Dateien des Mega-Kontos herunterladen, um ordnungsgemäß zu funktionieren. Aus diesem Grund ist der Zugriff auf Konten mit vielen Dateien möglicherweise langsam.</string>
<string name="filestoragename_content">Android-Dateibrowser</string>
<string name="filestorage_setup_title">Dateizugriff initialisieren</string>
<string name="database_location">Speicherort der Datenbank</string>
@@ -759,7 +760,7 @@ Anbei einige Tipps, die bei der Diagnose des Problems helfen können:\n
<string-array name="ChangeLog_1_09d">
<item>Unterstützung für das Ansehen, Entfernen und Wiederherstellen von Eintragssicherungen hinzugefügt</item>
<item>Unterstützung für MEGA-Cloudspeicher hinzugefügt </item>
<item>Unterstützung für Google Drive mit eingeschränktem Anwendungsbereich hinzugefügt</item>
<item>Unterstützung für Google Drive mit eingeschränktem Zugriff hinzugefügt</item>
</string-array>
<string-array name="ChangeLog_1_09c">
<item>Google-Drive-Authentifizierung erneut implementiert, Google-Drive-Unterstützung wieder aktiviert </item>

View File

@@ -719,6 +719,14 @@
<string name="EntryChannel_desc">Notificación para simplificar el acceso a la entrada seleccionada actualmente.</string>
<string name="CloseDbAfterFailedAttempts">Cierre la base de datos después de tres intentos fallidos de desbloqueo biométrico.</string>
<string name="WarnFingerprintInvalidated">¡Atención! La autenticación biométrica puede ser invalidada por Android, p. ej. después de añadir una nueva huella dactilar en los ajustes de su dispositivo. ¡Esté seguro de conocer siempre cómo desbloquear con su contraseña maestra!</string>
<string-array name="ChangeLog_1_12">
<item>Actualizado desde Xamarin Android a .net 8</item>
<item>Upgraded to Target SDK 34</item>
<item>Upgraded to Material 3 user interface</item>
<item>Improve autofill to work with Compose apps</item>
<item>Fix hostname matching in autofill and search</item>
<item>Fix issue with password generator</item>
</string-array>
<string-array name="ChangeLog_1_11">
<item>Añadidos botones de acción flotante para la búsqueda y vista general de TOTP (si las entradas TOTP están presentes).</item>
<item>Se ha mejorado la visualización de los campos TOTP añadiendo un indicador de tiempo de espera y mostrándolo de forma más destacada.</item>

View File

@@ -546,6 +546,7 @@
<string name="filestoragename_dropboxKP2A">Dropbox (folder KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Jeżeli nie chcesz dać KP2A pełnego dostępu do wszystkich folderów Dropbox, możesz wybrać tę opcję. Aplikacja zażąda dostępu jedynie do folderu Aplikacje/Keepass2Android. Jest to szczególnie przydatne podczas tworzenia nowej bazy danych. Jeżeli już posiadasz bazę danych, kliknij w tę opcję, aby utworzyć folder, następnie umieść swój plik w folderze (ze swojego PC) i wybierz tę opcję ponownie, aby otworzyć plik.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">Uwaga: Google ogranicza dostęp do Dysku Google dla coraz większej liczby użytkowników. Jeśli wbudowana obsługa Dysku Google nie działa, skorzystaj z systemowego wybierania plików i wybierz Dysk Google stamtąd!</string>
<string name="filestoragename_gdriveKP2A">Dysk Google (pliki KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Jeśli nie chcesz dać KP2A dostępu do pełnego Dysku Google, możesz wybrać tę opcję. Pamiętaj, że najpierw musisz utworzyć plik bazy danych, istniejące pliki nie są widoczne dla aplikacji. Wybierz tę opcję z ekranu tworzenia bazy danych lub, jeśli już otworzyłeś bazę danych, eksportując bazę danych wybierając tę opcję.</string>
<string name="filestoragename_pcloud">PCloud (KP2A folder)</string>
@@ -718,16 +719,33 @@
<string name="EntryChannel_desc">Powiadomienie ułatwiające dostęp do aktualnie wybranego wpisu.</string>
<string name="CloseDbAfterFailedAttempts">Zamknij bazę danych po trzech nieudanych próbach odblokowania biometrycznego.</string>
<string name="WarnFingerprintInvalidated">Uwaga! Uwierzytelnienie biometryczne może zostać unieważnione przez Androida, np. po dodaniu nowego odcisku palca w ustawieniach urządzenia. Upewnij się, że zawsze wiesz, jak odblokować przy użyciu hasła głównego!</string>
<string-array name="ChangeLog_1_12">
<item>Zaktualizowano z Xamarin Android do .net 8</item>
<item>Zaktualizowano Target SDK do 34</item>
<item>Zaktualizowano interfejs użytkownika do Material Design 3</item>
<item>Ulepszono autouzupełnianie, aby działało z aplikacjami opartymi na Compose</item>
<item>Naprawiono dopasowywanie hostów w autouzupełnianiu i wyszukiwaniu</item>
<item>Naprawiono problem związany z generatorem haseł</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>Zaktualizowano SDK OneDrive do wersji 5.68</item>
<item>Zaktualizowano SDK DropBox do wersji 7.0.0</item>
<item>Zaktualizowano Gradle, NewtonsoftJson, FluentFTP, MegaApiClient i okhttp</item>
<item>Naprawiono błąd w wybieraniu plików WebDAV</item>
</string-array>
<string-array name="ChangeLog_1_11">
<item>Dodano pływające przyciski szukania i podglądu TOTP (jeśli są wpisy TOTP).</item>
<item>Poprawa wyświetlania pól TOTP na wyraźniejsze i ze wskaźnikiem czasu.</item>
<item>TOTP są widoczne teraz w widoku grupy.</item>
<item>Skopiuj tekst do schowka po długim naciśnięciu w widoku wpisu.</item>
<item>TOTP łatwiej dostępne we wbudowanej klawiaturze.</item>
<item>Show entry notification when autofilling a TOTP entry. This allows to copy the TOTP to clipboard. See preferences to configure the behavior.</item>
<item>Updated TOTP implementation to resolve compatibility issues with KeePass2 and TrayTOTP</item>
<item>Pokaż powiadomienie przy automatycznym wypełnianiu wpisu TOTP. Pozwala to na skopiowanie TOTP do schowka. W ustawieniach możesz skonfigurować to zachowanie.</item>
<item>Zaktualizowano implementację TOTP, aby rozwiązać problemy związane z kompatybilnością z KeePass2 i TrayTOTP</item>
<item>Małe poprawki</item>
</string-array>
<string-array name="ChangeLog_1_11_net">
<item>Zaktualizuj SDK pCloud, aby zapewnić dostęp do udostępnionych folderów</item>
</string-array>
<string-array name="ChangeLog_1_10">
<item>Dodaj wsparcie dla uprawnień do powiadomień na Androidzie 13+</item>
<item>Poprawia wdrażania FTP i SFTP</item>
@@ -809,7 +827,189 @@
* Nowa implementacja dla OneDrive: zawiera wsparcie dla OneDrive for Business, plików współdzielonych, wybranych zakresów dostępu, wielu kont i naprawia problemy z dostępem offline\n
* Poprawki błędów
</string>
<string name="ChangeLog_1_07"> Wersja 1.07\n
* Naprawiono błędy na urządzeniach Samsunga z Androidem 9\n
* Pozwól na otwarcie więcej niż jednej bazy danych, kompatybilnej z KeeAutoExec\n
* SFTP: Możliwość na uwierzytelnianie kluczem publicznym, sprawdzanie zmiany klucza hosta\n
* Wprowadzono wsparcie dla pCloud - dzięki gilbsgilbs!\n
* Jawne wsparcie dla Nextcloud\n
* Ulepszono zapisywanie i aktualizację załączników do wpisów\n
* Więcej opcji dostosowania zachowania do preferencji użytkownika\n
* SSL: Zaufanie certyfikatom użytkownika\n
* Ulepszono autouzupełnianie (działa teraz w FireFox, umożliwia zredukowanie popupów)\n
* Poprawki błędów\n
</string>
<string name="ChangeLog_1_06">Wersja 1.06\n
* Zmieniono aplikację do obsługi Yubikey Challenge-Response z YubiChallenge na ykDroid.\n
* Dodano wsparcie dla Challenge-Response w formacie KeepassXC. Uwaga: Baza danych musi być w formacie KDBX4!\n
* Dodano opcję odrzucania ładowania plików z kosza w Google Drive\n
* Zmieniono implementację TLS dla FTPS, dodano obejście do błędu JSch z serwerami obsługującymi gssapi-with-mic\n
* Poprawki błędów\n
</string>
<string name="ChangeLog_1_05">Wersja 1.05\n
* Użyj kanału powiadomień w Androidzie 8 do konfiguracji, poprzez ustawienia systemu\n
* Ikona wpisu jest pokazywana w powiadomieniu\n
* Użyto adaptacyjnej ikony aplikacji dla Androida 8, używaj okrągłej ikony dla Androida 7\n
* Możliwość aktywnego wyszukiwania po odblokowaniu (zobacz w ustawieniach)\n
* Zmieniono sposób w jaki pliki są zapisywane przez Storage Access Framework, rozwiązano problemy z aktualizowaniem plików na Dysku Google otwieranych przez systemowy menadżer plików\n
* Dodano kilka tekstów informacyjnych, aby uniknąć częstych nieporozumień\n
* Tworzone są lokalne kopie zapasowe pomyślnie otwartych baz danych, aby zmniejszyć ryzyko utraty danych\n
* Zaktualizowano JSch do obsługi nowszych szyfrów SSH\n
* Zezwolono na edycję ustawień połączeń, na przykład kiedy hasło do WebDav zostanie zmienione\n
* Dodano wsparcie dla Yubikey Neo w trybie hasła statycznego\n
* Dodano możliwość wyłączenia sugestii autouzupełniania\n
* Naprawiono wyciek danych do logcat\n
* Poprawki błędów\n
</string>
<string name="ChangeLog_1_04b"> Wersja 1.04b\n
* Naprawienie crasha, gdy użytkownik próbuje włączyć autouzupełnianie na urządzeniach Huawei.\n
</string>
<string name="ChangeLog_1_04">Wersja 1.0.4 \n
* Dodano serwis autouzupełniania dla Androida 8.0 i dalej. \n
* Zaktualizowano biblioteki, narzędzia budownicze oraz wersję Target SDK \n</string>
<string name="ChangeLog_1_03">Wersja 1.03\n
* Usunięto usługę dostępności autouzupełniania, zgodnie z żądaniem Google. Proszę zobaczyć ustawienia dostępu do hasła, gdzie można znaleźć plugin replikujący poprzednią funkcjonalność.\n
* Dodano ponownie aplikacje firm trzecich jako opcję przechowywania plików\n
* Zintegrowano przeglądarkę obrazów do przeglądania zdjęć bez przesyłania ich do innej aplikacji\n
* Uaktualnienie OkHttp rozwiązuje problemy z niektórymi połączeniami\n
* Wsparcie dla wpisów KeeTrayTOTP, teraz obsługuje wpisy Steam\n
</string>
<string name="ChangeLog_1_02">Wersja 1.02\n
* Kilka ulepszeń zabezpieczeń. Dzięki raportowi bezpieczeństwa jean-baptiste.cayrou@thalesgroup.com i vincent.fargues@thalesgroup.com oraz ich współpracy!\n
* Wsparcie dla KeyboardSwapPlugin (zobacz Opcje dostępu do hasła): pozwala przełączać metody wprowadzania automatycznie na urządzeniach bez root-a. Podziękowania dla Mishaal Rahman z XDA-Developers za uczynienie tego możliwym.\n
* Poprawki dl ausługi ułatwień dostępu w ostatnich wersjach Chrome\n
* Naprawiono niepotrzebne czyszczenie danych linii papilarnych\n
* Poprawki drobnych awarii\n
* Zaktualizowano SDK Dropbox do zapewnienia przyszłej kompatybilności\n
* Usunięto raportowanie błędów za pośrednictwem Xamarin Insights\n
* Aktualizacja narzędzi kompilacji\n
</string>
<string name="ChangeLog_1_01g">Wersja 1.01-g\n
* Naprawiono awarię podczas próby pracy offline\n
* Naprawiono nieprawidłowe kodowanie poświadczeń FTP(S)\n
* Naprawiono awarię podczas korzystania z usługi OneDrive na starszych wersjach Androida\n
* Wyświetlanie czasu jako czas lokalny na ekranie wpisu\n
</string>
<string name="ChangeLog_1_01d">Wersja 1.01-d\n
* Poprawiono wyświetlanie listy plików OneDrive\n
* Zezwolono na ignorowanie błędy certyfikatu również kiedy weryfikacji nazwy hosta się nie powiedzie (niezalecane w wersji komercyjnej)\n
* Poprawka do okazjonalnych problemów z szybkim odblokoweaniem pomimo prawidłowego kodu\n </string>
<string name="ChangeLog_0_9_8c">Wersja 0.9.8c\n
* Poprawka luki bezpieczeństwa protokołu SSL w Microsoft Live SDK (używany podczas uzyskiwania dostępu do plików za pośrednictwem OneDrive)\n
* Poprawka błędu: Poprzednia wersja zawierała dwie metody wprowadzania (jedną niedziałającą poprawnie)\n </string>
<string name="ChangeLog_1_01"> Wersja 1.01\n
* dodano wsparcie dla nowego formatu KDBX-4 (kompatybilne z Keepass 2.35) włącznie z generowaniem kluczy Argon2 i szyfrowaniem ChaCha20.\n
* Przepisano przechowywanie plików WebDav, umożliwiając przeglądanie plików z wsparciem nowoczesnego szyfrowania.\n
* Przepisano przechowywanie plików FTP,, umożliwiając przechowywanie plików i z wsparciem szyfrowania (FTPS).\n
* Zaktualizowano SDK OneDrive (poprzednio używane Live SDK nie jest już aktualizowane)\n
* Zaktualizowano SDK Dropbox do wersji 2 (poprzednio używana wersja 1 SDK jest oznaczona jako przestarzała).\n
* Dodano wsparcie dla OwnCloud.\n
* Dodano prośbę o pozwolenie na dostęp do pamięci przed otwieraniem lokalnych plików
</string>
<string name="ChangeLog_1_0_0e"> Wersja 1.0.0e\n
* Naprawiono odblokowywanie odciskiem palca na starszych urządzeniach Samsung z Androidem 6\n
* Dodane natywne wsparcie dla urządzeń x86\n
* Zezwolono na chowanie klawiaturę programowej podczas skanowania odcisku palca\n
* Aktualizacja systemu budowania
</string>
<string name="ChangeLog_1_0_0">Wersja 1.0.0\n
* Odblokowywanie odciskiem palca (Android 6.0+ lub urządzenie Samsung)\n
* Dodano usługę autouzupełniania (Android 5.0+)\n
* Dodano wsparcie dla szablonów wiadomości\n
* Dodano tryb \"pracy offline\"\n
* Umożliwiono kopiowanie wpisów\n
* Tryb autouzupełniania dla nazw pól\n
* Umożliwiono usuwanie wpisów na liście ostatnich plików\n
* Prośba o uprawnienia w trakcie działania aplikacji w Androidzie 6.0\n
* Poprawki błędów (we wbudowanej klawiaturze, podczas wybierania ikon)\n
* Dodano opcję wysyłania raportów o błędach\n
* Dodano wskazówki dotyczące obsługi w kilku miejscach\n
</string>
<string name="ChangeLog_0_9_9"> Wersja 0.9.9\n
* Kompletnie przeprojektowany interfejs użytkownika. Wielkie podziękowania dla Stefano Pignataro (http://www.spstudio.at) za jego pomoc!\n
* Umożliwiono dodawanie własnych ikon\n
* Wsparcie dla trybu Multi Window na urządzeniach Samsung\n
* Zwiększono domyślną liczbę rund szyfrowania dla nowych baz danych\n
* Sprawdzanie czy nie duplikatów kluczy w dodatkowych polach w celu uniknięcia utraty danych\n
</string>
<string name="ChangeLog_0_9_9c"> Wersja 0.9.9c\n
* Powrócił ciemny motyw\n
* Możesz zainstalować inne paczki ikon (ikony w klasycznym stylu Windowsa są dostępne w sklepie Play)\n
* Dodano pytanie o potwierdzenie podczas usuwania elementów z pominięciem kosza\n
* Poprawki błędów (złe wyświetlanie kodowania tajnego klucza OTP, niewłaściwa ikona aplikacji w niektórych miejscach)\n
</string>
<string name="ChangeLog_0_9_8b"> Wersja 0.9.8b\n
* Poprawki błędów (Zapisywanie nie udawało się dla niektórych baz danych, nie działało eksportowanie do lokalnego urządzenia, wybieranie niektórych opcji wysypywało aplikację)\n
</string>
<string name="ChangeLog_0_9_8"> Wersja 0.9.8\n
* Wsparcie dla Storage Access Framework (umożliwia zapis na kartę SD i Google Drive w KP2A Offline)\n
* Próba wykrywania błędnych danych wprowadzanych przez użytkownika podczas wpisywania WebDAV URLs (katalog zamiast pliku)\n
* Zmieniono czcionkę hasła\n
* Umożliwiono zmianę konta Dropbox\n
* Poprawka błędu: Teraz hasło OTP jest zapamiętywane\n</string>
<string name="ChangeLog_0_9_7b"> Wersja 0.9.7b\n
* Zaktualizowano tłumaczenia\n
* Poprawki błędów: brakowało czcionki do haseł w wersji 0.9.7, sortowanie po nazwie nie sortowało grup\n
</string>
<string name="ChangeLog_0_9_7"> Wersja 0.9.7\n
* Dodano wsparcie dla zapisu bazy danych (kdb) dla Keepass 1 (beta!)\n
* Lepsze przełączanie do poprzedniej klawiatury (działa także na nie-zrootowanych urządzeniach)\n
* Wsparcie dla KeeChallenge ze zmienną długością wyzwań\n
* Zabezpieczono przed możliwością zrobienia zrzutów ekranu dla ekranów QuickUnlock i hasła\n
* Odwrócono kolejność sortowania dla Sortuj według daty modyfikacji (teraz malejąco)\n
* Poprawki błędów: Widok notatek jest teraz poprawnie aktualizowany po zmianach. Widoki haseł teraz chowają hasła poprawnie na (miejmy nadzieję) wszystkich urządzeniach; naprawiono błąd, który powodował, że można było dwukrotnie dodać ten sam wpis; naprawiono problem z pokazywaniem ostrzeżenia o zduplikowanych UUID nawet po naprawieniu bazy danych\n
</string>
<string name="ChangeLog_0_9_3_r5"> <b>Wersja 0.9.3 r5</b>\n
* Zawarto poprawki z Xamarin: Keepass2Android jest teraz kompatybilny z ART na Androidzie 4.4.2. Nareszcie!\n
* Poprawki błędów: błędy w synchronizacji (odswieżanie widoku, poprawne sprawdzanie zmian na http), błędy na urządzeniach z Androidem 2.x, błędy w implementacji Google Drive i SkyDrive, czyszczenie schowka przy zamykaniu bazy danych, błąd otwierania załączników, problemy z wyświetlaniem klawiatury\n
</string>
<string name="ChangeLog_0_9_3"><b>Wersja 0.9.3</b>\n
* Nowa klawiatura z wieloma usprawnieniami. Sprawdź ustawienia, aby dostosować.\n
* Wsparcie tylko do odczytu dla kdb (pliki Keepass 1). Eksperymentalne!\n
* Dodano wsparcie protokołu SFTP\n
* Dodano obejście błędu w ART (Android 4.4.2)\n
* Poprawki błędów\n</string>
<string name="ChangeLog_0_9_2"><b>Wersja 0.9.2</b>\n
* Dodano obsługę haseł jednorazowych (kompatybilne z wtyczką OtpKeyProv)\n
* Zintegrowano wparcie NFC dla haseł jednorazowych z YubiKey NEO \n
* Kilka usprawnień interfejsu\n
* Zintegrowano bibliotekę Keepass 2.24\n
* Dodano opcję do zabijania procesu aplikacji (sprawdź ustawienia)\n
* Usprawniona weryfikacja certyfikatów SSL\n
* Poprawki błędów\n</string>
<string name="ChangeLog_0_9_1"><b>Wersja 0.9.1</b>\n
* Dodano integrację ze SkyDrive (Tylko zwyczajna edycja Keepass2Android)\n
* Poprawiono problemy z integracją z Google Drive\n
* Dodano wsparcie dla NTLM
</string>
<string name="ChangeLog_0_8_1"><b>Wersja 0.8.1</b>\n
* KP2A w trybie Offline i \"Online\" może być zainstalowany równocześnie\n
* Dodane nowe tłumaczenia (Dziękujemy wszystkim tłumaczom!)</string>
<string name="ChangeLog_0_8"><b>Wersja 0.8</b>\n
* Ulepszono interfejs użytkownika, szczególnie na urządzeniach z Androidem 4.x\n
* Możliwość używania innych menedżerów plików w celu wyboru plików\n
* Dodano bezpieczniejszy sposób otwierania załączników (z pamięci podręcznej)\n
* Naprawiono błędy podczas \"Edycji\"\n
* Prawdopodobnie wprowadzono nowe błędy :-)</string>
<string name="ChangeLog_keptDonate">Rozszerzone możliwości darowania piwa lub czegoś innego</string>
<string name="ChangeLog_0_7"> <b>Wersja 0.7</b> \n * Zwiększono szybkość ładowania: transformacje kluczy teraz 10 razy szybsze\n
* Dodano wirtualną klawiaturę Keepass2Android: przełącz się na nią podczas wprowadzania danych logowania. Chroni przed podsłuchiwaniem haseł z użyciem schowka (wyłącz stare powiadomienia o schowku w ustawieniach).
* Dodano opcję postawienia mi piwa lub czegoś innego (zobacz w menu)
</string>
<string name="ChangeLog"> <b>Wersja 0.6.2</b>\n
* Integracja Google Drive/Dropbox/(...): Otworzenie pliku .kdbx w Google Drive lub Dropbox uruchomi teraz KP2A.\n
* Poprawiono okno wyszukiwania \n
* Ulepszono wyniki wyszukiwania dla udostępnionych adresów URL z subdomenami\n
* Dodano opcje przesyłania opinii, oceniania i tłumaczenia aplikacji w menu\n
\n
<b>Wersja 0.6.1</b>\n
* Wykrywanie zmian w bazie danych w tle (np. spowodowanych działaniem aplikacji synchronizującej)\n
* Ulepszone wyszukiwanie adresów URL z przeglądarki\n
* Potwierdzenie przed porzuceniem zmian\n
\n
<b>Wersja 0.6</b>\n
Pierwsze publiczne wydanie
</string>
<string-array name="clipboard_timeout_options">
<item>30 sekund</item>
<item>1 minuta</item>
@@ -900,5 +1100,9 @@
<string name="AutofillWarning_Intro">Zamierzasz wstawić poświadczenia dla domeny \"%1$s\" do aplikacji \"%2$s\".</string>
<string name="AutofillWarning_FillDomainInUntrustedApp">Jeśli ufasz \"%2$s\" należącym do \"%1$s\" lub ufasz aplikacji \"%2$s\", aby nie używać danych logowania (np. ponieważ jest to zaufana aplikacja przeglądarki), jest w porządku, aby kontynuować. Jeśli nie, proszę anulować.</string>
<string name="AutofillWarning_trustAsBrowser">Akceptuj zawsze w \"%1$s\"</string>
<string name="kp2a_switch_on_sendgodone">Przełącz klawiaturę po zakończeniu</string>
<string name="kp2a_switch_on_sendgodone_summary">Przełącz po naciśnięciu wyślij/idź/wykonaj</string>
<string name="qr_scanning_error_no_google_play_services">Skanowanie kodów QR wymaga usług Google Play. Zainstaluj lub zaktualizuj usługi Google Play na swoim urządzeniu.</string>
<string name="english_ime_settings">Ustawienia klawiatury Keepass2Android</string>
<string name="switch_keyboard_inside_kp2a_enabled">Uwaga: Włączono w ustawieniach Aplikacja -> Bezpieczeństwo, opcję „Używaj wbudowanej klawiatury w Keepass2Android”. Może to powodować pojawianie się tego okna przy otwieraniu aplikacji lub edycji wpisu.</string>
</resources>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="about_feedback">反馈</string>
<string name="AboutText">Keepass2Android 是一款支持 Keepass 2.x 数据库读写的密码管理应用。</string>
<string name="AboutText">Keepass2Android 是一款支持 Keepass 2.x 数据库读写的Android平台的密码管理应用。</string>
<string name="CreditsText">用户界面基于 Brian Pellin 开发的 KeepassDroid 项目。数据库操作代码基于 Dominik Reichl 开发的 KeePass 项目。Android 机器人图案根据 Google 创建和共享的作品进行复制或修改,并依照知识共享署名 3.0 协议进行使用。</string>
<string name="CreditsTextSFTP">SFTP 支持由 JCraft, Inc. 创建的 JSch 库(基于 BSD 许可证)实现</string>
<string name="CreditsIcons">锤子图标是由 John Caserta 通过 Noun Project 创建的。企鹅图标是由 Adriano Emerick 通过 Noun Project 创建的。羽毛图标是由 Jon Testa 通过 Noun Project 创建的。苹果图标是由 Ava Rowell 通过 Noun Project 创建的。图片图标来自 https://icons8.com/icon/5570/Picture 。</string>
@@ -725,9 +725,15 @@
<item></item>
<item>Upgraded to Material 3 user interface</item>
<item>Improve autofill to work with Compose apps</item>
<item>Fix hostname matching in autofill and search</item>
<item>修复在自动填充和搜索时主机名的匹配问题</item>
<item>Fix issue with password generator</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>Upgraded OneDrive SDK to version 5.68</item>
<item>Upgraded Dropbox SDK to version 7.0.0</item>
<item>Upgraded Gradle, NewtonsoftJson, FluentFTP, MegaApiClient and okhttp</item>
<item>修复WebDav中文件选择的错误</item>
</string-array>
<string-array name="ChangeLog_1_11">
<item>添加了用于搜索和概览 TOTP 的浮动按钮如果存在TOTP条目</item>
<item>通过添加超时指示器并突出显示,改进 TOTP 字段的显示效果</item>

View File

@@ -209,6 +209,7 @@
<string name="ShowUnlockedNotification_key">ShowUnlockedNotification</string>
<bool name="ShowUnlockedNotification_default">true</bool>
<integer name="WebDavChunkedUploadSize_default">65536</integer>
<string name="PreloadDatabaseEnabled_key">PreloadDatabaseEnabled</string>
<bool name="PreloadDatabaseEnabled_default">true</bool>

View File

@@ -93,6 +93,8 @@
<string name="disable_fingerprint_unlock">Disable 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="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_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>
@@ -319,6 +321,7 @@
<string name="QuickUnlock_label_secure">Enter QuickUnlock code:</string>
<string name="QuickUnlock_button">QuickUnlock!</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_summary">Defines whether QuickUnlock is enabled by default or not.</string>
<string name="ViewDatabaseSecure_title">Protect database display</string>
@@ -726,6 +729,16 @@
<string name="EntryChannel_desc">Notification to simplify access to the currently selected entry.</string>
<string name="CloseDbAfterFailedAttempts">Close database after three failed biometric unlock attempts.</string>
<string name="WarnFingerprintInvalidated">Warning! Biometric authentication can be invalidated by Android, e.g. after adding a new fingerprint in your device settings. Make sure you always know how to unlock with your master password!</string>
<string name="webdav_chunked_upload_size_title">Chunk size for WebDav upload</string>
<string name="webdav_chunked_upload_size_summary">Size of chunks when uploading to WebDav servers in bytes. Use 0 to disable chunked upload.</string>
<string-array name="ChangeLog_1_13">
<item>Improved password quality estimation by considering most popular passwords.</item>
<item>Block password-based QuickUnlock (for security reasons) if the device does not have a screen lock activated.</item>
<item>Update network security configuration to disable clear-text transfer.</item>
</string-array>
<string-array name="ChangeLog_1_12">
<item>Upgraded from Xamarin Android to .net 8</item>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<base-config>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />

View File

@@ -45,6 +45,14 @@
android:title="@string/UseFileTransactions_title"
android:key="@string/UseFileTransactions_key" />
<EditTextPreference
android:key="WebDavChunkedUploadSize_str"
android:title="@string/webdav_chunked_upload_size_title"
android:summary="@string/webdav_chunked_upload_size_title"
android:defaultValue="@integer/WebDavChunkedUploadSize_default"
android:inputType="number"
/>
<CheckBoxPreference
android:enabled="true"
android:persistent="true"
@@ -81,4 +89,5 @@
android:title="@string/CheckForDuplicateUuids_title"
android:key="@string/CheckForDuplicateUuids_key" />
</PreferenceScreen>

View File

@@ -351,6 +351,9 @@ namespace keepass2android
QuickUnlockEnabled = enabled;
}
public bool ScreenLockWasEnabledWhenOpeningDatabase { get; set; }
public bool QuickUnlockEnabled { get; private set; }
public int QuickUnlockKeyLength { get; private set; }
@@ -833,8 +836,8 @@ namespace keepass2android
new AndroidContentStorage(LocaleManager.LocalizedAppContext),
#if !EXCLUDE_JAVAFILESTORAGE
#if !NoNet
new DropboxFileStorage(LocaleManager.LocalizedAppContext, this),
new DropboxAppFolderFileStorage(LocaleManager.LocalizedAppContext, this),
DropboxFileStorage.IsConfigured ? new DropboxFileStorage(LocaleManager.LocalizedAppContext, this) : null,
DropboxAppFolderFileStorage.IsConfigured ? new DropboxAppFolderFileStorage(LocaleManager.LocalizedAppContext, this): null,
GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(LocaleManager.LocalizedAppContext)==ConnectionResult.Success ? new GoogleDriveFileStorage(LocaleManager.LocalizedAppContext, this) : null,
GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(LocaleManager.LocalizedAppContext)==ConnectionResult.Success ? new GoogleDriveAppDataFileStorage(LocaleManager.LocalizedAppContext, this) : null,
new OneDriveFileStorage(this),
@@ -843,7 +846,7 @@ namespace keepass2android
new OneDrive2AppFolderFileStorage(),
new SftpFileStorage(LocaleManager.LocalizedAppContext, this, IsFtpDebugEnabled()),
new NetFtpFileStorage(LocaleManager.LocalizedAppContext, this, IsFtpDebugEnabled),
new WebDavFileStorage(this),
new WebDavFileStorage(this, WebDavChunkedUploadSize),
new PCloudFileStorage(LocaleManager.LocalizedAppContext, this),
new PCloudFileStorageAll(LocaleManager.LocalizedAppContext, this),
new MegaFileStorage(App.Context),
@@ -1330,6 +1333,18 @@ namespace keepass2android
}
}
public int WebDavChunkedUploadSize
{
get
{
return int.Parse(PreferenceManager.GetDefaultSharedPreferences(LocaleManager.LocalizedAppContext)
.GetString("WebDavChunkedUploadSize_str",
LocaleManager.LocalizedAppContext.Resources
.GetInteger(Resource.Integer.WebDavChunkedUploadSize_default).ToString()));
}
}
}
@@ -1455,7 +1470,6 @@ namespace keepass2android
{
Kp2aLog.LogUnexpectedError(e.Exception);
}
}
}