aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore21
-rw-r--r--.travis.yml2
-rw-r--r--OpenKeychain/build.gradle181
-rw-r--r--OpenKeychain/proguard-rules.pro41
-rw-r--r--OpenKeychain/src/main/AndroidManifest.xml35
-rw-r--r--OpenKeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java16
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java63
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java58
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/InputDataOperation.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java80
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java121
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java42
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java31
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryFileProvider.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/receiver/NetworkReceiver.java52
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java57
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/CardException.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyType.java65
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java113
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java757
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java58
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbTransport.java304
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbTransportException.java37
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java16
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java32
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java202
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java180
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java53
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java95
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java15
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java103
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiUserIdsFragment.java223
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java118
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java176
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java21
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java37
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java827
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java473
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java37
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java77
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java27
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OkHttpClientFactory.java76
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OkHttpKeybaseClient.java51
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/UsbConnectionDispatcher.java97
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.pngbin0 -> 923 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.pngbin0 -> 623 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_change_grey_24dp.pngbin0 -> 1066 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.pngbin0 -> 1612 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.pngbin0 -> 2223 bytes
-rw-r--r--OpenKeychain/src/main/res/layout-land/backup_code_fragment.xml199
-rw-r--r--OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml459
-rw-r--r--OpenKeychain/src/main/res/layout-w400dp/passphrase_dialog_backup_code.xml201
-rw-r--r--OpenKeychain/src/main/res/layout-w600dp/backup_code_fragment.xml459
-rw-r--r--OpenKeychain/src/main/res/layout-w600dp/security_token_operation_activity.xml (renamed from OpenKeychain/src/main/res/layout-land/security_token_operation_activity.xml)0
-rw-r--r--OpenKeychain/src/main/res/layout/add_subkey_dialog.xml163
-rw-r--r--OpenKeychain/src/main/res/layout/backup_code_fragment.xml346
-rw-r--r--OpenKeychain/src/main/res/layout/certify_item.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/certify_key_fragment.xml5
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_final_fragment.xml6
-rw-r--r--OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml10
-rw-r--r--OpenKeychain/src/main/res/layout/multi_user_ids_fragment.xml6
-rw-r--r--OpenKeychain/src/main/res/layout/passphrase_dialog_backup_code.xml175
-rw-r--r--OpenKeychain/src/main/res/layout/two_line_spinner_dropdown_item.xml32
-rw-r--r--OpenKeychain/src/main/res/layout/upload_key_activity.xml6
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_changelog.md6
-rw-r--r--OpenKeychain/src/main/res/raw-eu/help_changelog.md6
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_changelog.md6
-rw-r--r--OpenKeychain/src/main/res/raw-it/advanced.md14
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_about.md6
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_changelog.md24
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_changelog.md6
-rw-r--r--OpenKeychain/src/main/res/raw-la/advanced.md9
-rw-r--r--OpenKeychain/src/main/res/raw-la/help_about.md72
-rw-r--r--OpenKeychain/src/main/res/raw-la/help_certification.md28
-rw-r--r--OpenKeychain/src/main/res/raw-la/help_changelog.md326
-rw-r--r--OpenKeychain/src/main/res/raw-la/help_start.md16
-rw-r--r--OpenKeychain/src/main/res/raw-nb/advanced.md14
-rw-r--r--OpenKeychain/src/main/res/raw-nb/help_about.md20
-rw-r--r--OpenKeychain/src/main/res/raw-nb/help_certification.md24
-rw-r--r--OpenKeychain/src/main/res/raw-nb/help_changelog.md60
-rw-r--r--OpenKeychain/src/main/res/raw-nb/help_start.md18
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/advanced.md14
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/help_about.md22
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/help_certification.md38
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/help_changelog.md14
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/help_start.md24
-rw-r--r--OpenKeychain/src/main/res/raw-ru/advanced.md12
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_about.md6
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_certification.md10
-rw-r--r--OpenKeychain/src/main/res/raw-sr/help_changelog.md6
-rw-r--r--OpenKeychain/src/main/res/raw-zh-rTW/advanced.md14
-rw-r--r--OpenKeychain/src/main/res/raw-zh/advanced.md6
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_certification.md4
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_changelog.md14
-rw-r--r--OpenKeychain/src/main/res/raw/help_faq.md2
-rw-r--r--OpenKeychain/src/main/res/values-cs/strings.xml11
-rw-r--r--OpenKeychain/src/main/res/values-de/strings.xml447
-rw-r--r--OpenKeychain/src/main/res/values-es-rMX/strings.xml10
-rw-r--r--OpenKeychain/src/main/res/values-es/strings.xml11
-rw-r--r--OpenKeychain/src/main/res/values-eu/strings.xml30
-rw-r--r--OpenKeychain/src/main/res/values-fa/strings.xml5
-rw-r--r--OpenKeychain/src/main/res/values-fi/strings.xml10
-rw-r--r--OpenKeychain/src/main/res/values-fr/strings.xml48
-rw-r--r--OpenKeychain/src/main/res/values-hi/strings.xml2
-rw-r--r--OpenKeychain/src/main/res/values-hu/strings.xml1
-rw-r--r--OpenKeychain/src/main/res/values-id/strings.xml2
-rw-r--r--OpenKeychain/src/main/res/values-it/strings.xml139
-rw-r--r--OpenKeychain/src/main/res/values-ja/strings.xml36
-rw-r--r--OpenKeychain/src/main/res/values-kn/strings.xml1
-rw-r--r--OpenKeychain/src/main/res/values-ko/strings.xml10
-rw-r--r--OpenKeychain/src/main/res/values-la/strings.xml87
-rw-r--r--OpenKeychain/src/main/res/values-nb/strings.xml117
-rw-r--r--OpenKeychain/src/main/res/values-nl/strings.xml11
-rw-r--r--OpenKeychain/src/main/res/values-pl/strings.xml11
-rw-r--r--OpenKeychain/src/main/res/values-pt-rBR/strings.xml407
-rw-r--r--OpenKeychain/src/main/res/values-ru/strings.xml473
-rw-r--r--OpenKeychain/src/main/res/values-sl/strings.xml11
-rw-r--r--OpenKeychain/src/main/res/values-sr/strings.xml22
-rw-r--r--OpenKeychain/src/main/res/values-sv/strings.xml52
-rw-r--r--OpenKeychain/src/main/res/values-sw360dp/styles.xml9
-rw-r--r--OpenKeychain/src/main/res/values-sw400dp/styles.xml9
-rw-r--r--OpenKeychain/src/main/res/values-tr/strings.xml11
-rw-r--r--OpenKeychain/src/main/res/values-uk/strings.xml11
-rw-r--r--OpenKeychain/src/main/res/values-vi/strings.xml1
-rw-r--r--OpenKeychain/src/main/res/values-zh-rTW/strings.xml12
-rw-r--r--OpenKeychain/src/main/res/values-zh/strings.xml52
-rw-r--r--OpenKeychain/src/main/res/values/colors.xml1
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml62
-rw-r--r--OpenKeychain/src/main/res/values/styles.xml7
-rw-r--r--OpenKeychain/src/main/res/xml/sync_preferences.xml6
-rw-r--r--OpenKeychain/src/main/res/xml/usb_device_filter.xml27
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java7
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java53
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/SplitUserIdTest.java (renamed from OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/KeyRingTest.java)37
-rw-r--r--README.md7
-rw-r--r--build.gradle15
m---------extern/KeybaseLib0
m---------extern/openpgp-api-lib0
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin53323 -> 53639 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xgradlew10
-rw-r--r--gradlew.bat2
-rw-r--r--settings.gradle18
195 files changed, 7855 insertions, 3449 deletions
diff --git a/.gitignore b/.gitignore
index 68f5b5a9e..c7c85bfa0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,37 +1,28 @@
-#Android specific
-bin
-gen
-obj
-libs/armeabi
+# Android specific
lint.xml
local.properties
release.properties
-ant.properties
*.class
*.apk
-#Gradle
+# Gradle
.gradle
build
-gradle.properties
-# this is in here because the prepare-tests thing modifies it, and we DON'T
-# want this to be commited. use git add -f to work on this file.
-settings.gradle
-#Maven
+# Maven
target
pom.xml.*
-#Eclipse
+# Eclipse
.project
.classpath
.settings
.metadata
-#IntelliJ IDEA
+# IntelliJ IDEA
.idea
*.iml
-#OS Specific
+# OS Specific
[Tt]humbs.db
.DS_Store
diff --git a/.travis.yml b/.travis.yml
index 402bfa4b8..9d87e5e7f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,6 +14,8 @@ addons:
hostname: short-hostname
android:
components:
+ - tools
+ - build-tools-23.0.2
- build-tools-23.0.1
- android-23
- android-22
diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle
index 0eedb8394..8fd7103f0 100644
--- a/OpenKeychain/build.gradle
+++ b/OpenKeychain/build.gradle
@@ -8,128 +8,127 @@ dependencies {
// NOTE: libraries are pinned to a specific build, see below
// from local Android SDK
- compile 'com.android.support:support-v4:23.2.0'
- compile 'com.android.support:appcompat-v7:23.2.0'
- compile 'com.android.support:design:23.2.0'
- compile 'com.android.support:recyclerview-v7:23.2.0'
- compile 'com.android.support:cardview-v7:23.2.0'
-
- // Unit tests in the local JVM with Robolectric
- // https://developer.android.com/training/testing/unit-testing/local-unit-tests.html
- // http://robolectric.org/getting-started/
- // http://www.vogella.com/tutorials/Robolectric/article.html
- testCompile 'junit:junit:4.12'
- testCompile ('org.robolectric:robolectric:3.0') {
- exclude group: 'org.bouncycastle', module: 'bcprov-jdk16'
- }
- testCompile 'org.mockito:mockito-core:1.10.19'
-
- // UI testing with Espresso
- androidTestCompile 'com.android.support.test:runner:0.4.1'
- androidTestCompile 'com.android.support.test:rules:0.4.1'
- androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
- androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
- androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2.1') {
- exclude group: 'com.android.support', module: 'appcompat'
- exclude group: 'com.android.support', module: 'support-v4'
- exclude module: 'recyclerview-v7'
- }
-
- // Temporary workaround for bug: https://code.google.com/p/android-test-kit/issues/detail?id=136
- // from https://github.com/googlesamples/android-testing/blob/master/build.gradle#L21
- configurations.all {
- resolutionStrategy.force 'com.android.support:support-annotations:23.2.0'
- }
+ compile 'com.android.support:support-v4:23.3.0'
+ compile 'com.android.support:appcompat-v7:23.3.0'
+ compile 'com.android.support:design:23.3.0'
+ compile 'com.android.support:recyclerview-v7:23.3.0'
+ compile 'com.android.support:cardview-v7:23.3.0'
+ compile 'com.android.support:support-annotations:23.3.0'
// JCenter etc.
- compile 'com.eftimoff:android-patternview:1.0.5@aar'
- compile 'com.journeyapps:zxing-android-embedded:3.1.0@aar'
+ compile 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
compile 'com.google.zxing:core:3.2.1'
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
compile 'com.getbase:floatingactionbutton:1.10.1'
compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0'
compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
- compile 'com.splitwise:tokenautocomplete:2.0.2@aar'
- compile 'com.github.pinball83:masked-edittext:1.0.3'
+ compile 'com.splitwise:tokenautocomplete:2.0.7@aar'
compile 'se.emilsjolander:stickylistheaders:2.7.0'
- compile 'org.sufficientlysecure:html-textview:1.3'
+ compile 'org.sufficientlysecure:html-textview:1.5'
compile 'org.sufficientlysecure:donations:2.4'
compile 'com.nispok:snackbar:2.11.0'
- compile 'com.squareup.okhttp:okhttp:2.7.1'
- compile 'com.squareup.okhttp:okhttp-urlconnection:2.7.1'
+ compile 'com.squareup.okhttp3:okhttp:3.2.0'
+ compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0'
compile 'org.apache.james:apache-mime4j-core:0.7.2'
compile 'org.apache.james:apache-mime4j-dom:0.7.2'
compile 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0'
compile 'com.cocosw:bottomsheet:1.3.0@aar'
// Material Drawer
- compile 'com.mikepenz:materialdrawer:4.6.3@aar'
- compile 'com.mikepenz:materialize:0.5.1'
- compile 'com.mikepenz:iconics-core:2.5.3@aar'
- compile 'com.mikepenz:google-material-typeface:2.2.0.1@aar'
- compile 'com.mikepenz:fontawesome-typeface:4.5.0.1@aar'
- compile 'com.mikepenz:community-material-typeface:1.3.41.1@aar'
+ compile 'com.mikepenz:materialdrawer:5.2.2@aar'
+ compile 'com.mikepenz:fastadapter:1.4.4'
+ compile 'com.mikepenz:materialize:0.8.8'
+ compile 'com.mikepenz:iconics-core:2.5.11@aar'
+ compile 'com.mikepenz:google-material-typeface:2.2.0.1.original@aar'
+ compile 'com.mikepenz:fontawesome-typeface:4.6.0.1@aar'
+ compile 'com.mikepenz:community-material-typeface:1.5.54.1@aar'
// Nordpol
compile 'com.fidesmo:nordpol-android:0.1.18'
// libs as submodules
- compile project(':extern:openpgp-api-lib:openpgp-api')
- compile project(':extern:openkeychain-api-lib:openkeychain-intents')
+ compile project(':openpgp-api-lib')
+ compile project(':openkeychain-api-lib')
compile project(':extern:bouncycastle:core')
compile project(':extern:bouncycastle:pg')
compile project(':extern:bouncycastle:prov')
compile project(':extern:minidns')
- compile project(':extern:KeybaseLib:Lib')
- compile project(':extern:safeslinger-exchange:safeslinger-exchange')
+ compile project(':KeybaseLib')
+ compile project(':safeslinger-exchange')
+
+ // Unit tests in the local JVM with Robolectric
+ // https://developer.android.com/training/testing/unit-testing/local-unit-tests.html
+ // http://robolectric.org/getting-started/
+ // http://www.vogella.com/tutorials/Robolectric/article.html
+ testCompile 'junit:junit:4.12'
+ testCompile ('org.robolectric:robolectric:3.0') {
+ exclude group: 'org.bouncycastle', module: 'bcprov-jdk16'
+ }
+ testCompile 'org.mockito:mockito-core:1.10.19'
+
+ // UI testing with Espresso
+ // Force usage of support libs in the test app, since they are internally used by the runner module.
+ // https://github.com/googlesamples/android-testing/blob/master/ui/espresso/BasicSample/app/build.gradle#L28
+ androidTestCompile 'com.android.support:support-annotations:23.3.0'
+ androidTestCompile 'com.android.support:appcompat-v7:23.3.0'
+ androidTestCompile 'com.android.support:design:23.3.0'
+ androidTestCompile 'com.android.support.test:runner:0.5'
+ androidTestCompile 'com.android.support.test:rules:0.5'
+ androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
+ androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
+ androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2.2') {
+ exclude group: 'com.android.support', module: 'appcompat'
+ exclude group: 'com.android.support', module: 'support-v4'
+ exclude module: 'recyclerview-v7'
+ }
+
}
// Output of ./gradlew -q calculateChecksums
// Comment out the libs referenced as git submodules!
dependencyVerification {
verify = [
- 'com.android.support:support-v4:992666398b80724a2f95ea3d7bf7c3d94fb810dcba7ae1aa6e38c0d6120d2603',
- 'com.android.support:appcompat-v7:14ab04eb2e3f302a082b79c308f6283d21909d1feb831a4e117cdacdad70adb7',
- 'com.android.support:design:e7f2a383ba675cf65d834f27079630a1e373f2f3a08330bec6a7c798ef50b3b2',
- 'com.android.support:recyclerview-v7:81aad9ff4104a20d8d5cda49dbbe0f2925e7dcd607a689374db843c6f2eca20a',
- 'com.android.support:cardview-v7:b2c2c070a78fbf7683ab4d84b23f9eecf2af3848f126775d048ae62238b55aed',
- 'com.eftimoff:android-patternview:594dde382fb9a445ef0c92d614f6f127727ce699f124de8167929e10f298bf8b',
- 'com.journeyapps:zxing-android-embedded:90840a4457e68962fdfb74f691c6a736be7596291001045241901f1f0e6db2ac',
+ 'com.android.support:support-v4:1e8b7cc1cb3d6f6a2fd913791a6313df6bbaa470be450384474e906bc234bd49',
+ 'com.android.support:appcompat-v7:dce81c41f76d83fa315617f4bc8ef2f84c5aa54b686f37b559422b939f622490',
+ 'com.android.support:design:1023c9d1ca3ecae3e0c54b7678cd275032fe5720bbb4f58e3c8b2f2a595d0051',
+ 'com.android.support:recyclerview-v7:32b98ca177d9352b19a92af80ff9a3c3589b50232b6722d2bce5514abe90be51',
+ 'com.android.support:cardview-v7:2f592da4dd1db85dab99653474dd139135aa3ca7596dfdbcfef714e0339b603a',
+ 'com.android.support:support-annotations:e9e076f3ea4fb144387c6054a6f69a2f6150ad4b1907897aaf55d6e8f4b8b91e',
+ 'com.journeyapps:zxing-android-embedded:afe4cd51d95ba0fd3a4bfe08c5a160bd32602aa174d511600ac824b6de4c79f1',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.getbase:floatingactionbutton:3edefa511aac4d90794c7b0496aca59cff2eee1e32679247b4f85acbeee05240',
'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13',
'org.ocpsoft.prettytime:prettytime:ef7098d973ae78b57d1a22dc37d3b8a771bf030301300e24055d676b6cdc5e75',
- 'com.splitwise:tokenautocomplete:2fc238424130b42155b5f2e39799a90bbbd13b148850afbe534ab08bb913c7f7',
- 'com.github.pinball83:masked-edittext:b1913d86482c7066ebb7831696773ac131865dc441cf8a3fc41d3b7d5691724e',
+ 'com.splitwise:tokenautocomplete:f56239588390f103b270b7c12361d99b06313a5a0410dc7f66e241ac4baf9baa',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
+ 'org.sufficientlysecure:html-textview:b3799eea5f440b32bdec18360e4ce66f6f77814c8df9d382718506a7da0de961',
'org.sufficientlysecure:donations:96f8197bab26dfe41900d824f10f8f1914519cd62eedb77bdac5b223eccdf0a6',
- 'org.sufficientlysecure:html-textview:39048e35894e582adada388e6c00631803283f8defed8e07ad58a5f284f272ee',
- 'com.squareup.okhttp:okhttp:8df336e3e93b22ba8c05da5d94caf968950db845869c7ca16ed682e065135aa8',
'com.nispok:snackbar:46b5eb9d630d329e13c2ce00ee9fb115ffb66c23c72cff32ee97eedd76824c6f',
+ 'com.squareup.okhttp3:okhttp:a41cdb7b024c56436a21e38f00b4d12e3b7e01451ffe6c4f545acba805bba03b',
+ 'com.squareup.okhttp3:okhttp-urlconnection:7d6598a6665c166e2d4b78956a96056b9be7de192b3c923ccf4695d08e580833',
'org.apache.james:apache-mime4j-core:4d7434c68f94b81a253c12f28e6bbb4d6239c361d6086a46e22e594bb43ac660',
- 'com.squareup.okhttp:okhttp-urlconnection:8dce03792fd7b5f089dc4fc0fdcecbbe50ae6cca21cb08a787a3b902a9914111',
- 'org.thoughtcrime.ssl.pinning:AndroidPinning:afa1d74e699257fa75cb109ff29bac50726ef269c6e306bdeffe8223cee06ef4',
'org.apache.james:apache-mime4j-dom:7e6b06ee164a1c21b7e477249ea0b74a18fddce44764e5764085f58dd8c34633',
- 'com.mikepenz:materialdrawer:4e2644f454cc2ce48b956536d3339957c3f592adb2e0b6dad72d477da29f7677',
+ 'org.thoughtcrime.ssl.pinning:AndroidPinning:afa1d74e699257fa75cb109ff29bac50726ef269c6e306bdeffe8223cee06ef4',
'com.cocosw:bottomsheet:4af6112a7f4cad4e2b70e5fdf1edc39f51275523a0f53011a012837dc103e597',
- 'com.mikepenz:iconics-core:d2495547db9d881168b1b502b1934f6a000ed5086c6c6a7114f3bbcbbb7ec306',
- 'com.mikepenz:materialize:2457dbe0b874a422c0a21bc6716cf5af1d5a8d39387857ff7c20855ab5543bf8',
- 'com.mikepenz:fontawesome-typeface:69cb09934a83bac607e78a29459868d537f766224b4a65a042d1f84c98c7b05d',
- 'com.mikepenz:google-material-typeface:48b2712de87d542e9b050846e9f602238a367f38e2d5e8ea4557c5b12adfcbec',
+ 'com.mikepenz:materialdrawer:4169462fdde042e2bb53a7c2b4e2334d569d16b2020781ee05741b50e1a2967d',
+ 'com.mikepenz:fastadapter:1bfc00216d71dfdfe0d8e7a9d92bb97bfaa1794543930e34b1f79d5d7adbddf6',
+ 'com.mikepenz:materialize:575195b2fa5b2414fb14a59470ee21d8a8cd8355b651e0cf52e477e3ff1cd96c',
+ 'com.mikepenz:iconics-core:d57c6b0ecb33d9ed9708da62e07ad8993d80a16f374f7a2018be7837a60b7ed7',
+ 'com.mikepenz:google-material-typeface:47eabb0aadcc0f56530c3ca462b671a5cf5251101cef8f581aedf90ca511914a',
+ 'com.mikepenz:fontawesome-typeface:033cf3460d8074bd37a1fefc2ff4eac8f2e3db835ec78bf386d46710e4d0827c',
+ 'com.mikepenz:community-material-typeface:382e8446fc08fe03cb1e0f91ee329ffd514c113ad22f8389b88424ac71ed5fbb',
'com.fidesmo:nordpol-android:56f43fe2b1676817bcb4085926de14a08282ef6729c855c198d81aec62b20d65',
- 'com.mikepenz:community-material-typeface:990acfcfb892a733d36748fe29176bd61dd5ab34bc8ca1c591200e639d955b99',
-// 'OpenKeychain.extern.bouncycastle:core:b22dfb37e09fb520683dd0ba089351787560a75b59b60822143f633ec984cab5',
-// 'OpenKeychain.extern.openpgp-api-lib:openpgp-api:fbd9a53022747bdc3c5b0926c6f9cbcbee19e766dd96f090a4310dc65026b393',
-// 'OpenKeychain.extern.openkeychain-api-lib:openkeychain-intents:9263330c00497b7bb70502160f50c8396228129376f48f4f5656d28360a2edac',
-// 'OpenKeychain.extern.bouncycastle:prov:2d93a52e1b519995b18c0a92a1e59a2773d67d9b466a9cce6af5202a66502577',
-// 'OpenKeychain.extern.bouncycastle:pg:1397025acf36be36d329c0345b136af776be82fe5d6dad70cc06db09d2f02201',
-// 'OpenKeychain.extern.safeslinger-exchange:safeslinger-exchange:989fcc0eba663489a41aa166f6bb39f21271d980faddea5f06ab75339e792d10',
- 'com.android.support:support-annotations:7f21659b084da073b77b6f7fe7ab250c4f23346238d4efdbbbb937e017ae4693',
-// 'OpenKeychain.extern:minidns:109d5851ab351d7628ed62a0ed96b40598952424e56657c17debbeb4a704f0ce',
-// 'OpenKeychain.extern.KeybaseLib:Lib:c5b1567ff781c311240e83f865c4ba76ae435eb00994529b8364371abf0d76de',
- 'com.android.support:animated-vector-drawable:4d8366192dedc8ca9e6ff62e9be9ba6145166554d61befc9df312e8328836f55',
- 'com.android.support:support-vector-drawable:0f43fc47b0d2797c4e1851aba61ab87a4fd33323348c2bd022821aaac052a266',
+// 'OpenKeychain:openpgp-api-lib:e4f456b77f80886eb2094bd643c8c8c9cf0eb3e8ef919706d7e92da6fc3c5517',
+// 'OpenKeychain:openkeychain-api-lib:cddb2953fc3ec2876923f01acbe91e136eaad1c327450a4fab21274b0edf73d9',
+// 'OpenKeychain.extern.bouncycastle:core:d4574e14e78d78b8b0b30b37089f82901a8fb46679915e034fe5d15cc5431c6a',
+// 'OpenKeychain.extern.bouncycastle:pg:064250718891c89d3bfa1e9bdef68bbf1cd2adb83532707754390b42643f2156',
+// 'OpenKeychain.extern.bouncycastle:prov:6933802a03ffd53c084415550db834a59540327434a061af39442e00002611f8',
+// 'OpenKeychain.extern:minidns:357901cf1b93f74f9729f6e3a1dfa5f148d627bf23423c8597ab82be84b3dc9d',
+// 'OpenKeychain:KeybaseLib:cbba456bf50083cde99f9c460fcbd812dbcd7d9e1a5c1df0ee9e080a85802aa8',
+// 'OpenKeychain:safeslinger-exchange:422efcad6868e32ce1446d18f08d728d266a30725fdc09a71f1b78c133bfc051',
+ 'com.android.support:support-vector-drawable:a4feae56880e385e147dfed3f73593ce61164ec49bb401f34de9a77a17c68a44',
+ 'com.android.support:animated-vector-drawable:ddf1f9dc18d38b21b01c21842d4bdf57613ea86398ede8f914f29b5770f4c32b',
'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266',
'com.fidesmo:nordpol-core:3de58e850a00bba5b4d3a604d1399bcd89f695ea191ec0b03a57222e18062d15',
]
@@ -141,14 +140,12 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
- // TODO: remove org.apache dependencies in LinkedTokenResource etc.
- useLibrary 'org.apache.http.legacy'
defaultConfig {
minSdkVersion 15
targetSdkVersion 23
- versionCode 39000
- versionName "3.9"
+ versionCode 39510
+ versionName "3.9.5"
applicationId "org.sufficientlysecure.keychain"
// the androidjunitrunner is broken regarding coverage, see here:
// https://code.google.com/p/android/issues/detail?id=170607
@@ -190,6 +187,12 @@ android {
}
debug {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ // Enable code coverage (Jacoco)
+ testCoverageEnabled true
+
applicationIdSuffix ".debug"
// Reference them in the java files with e.g. BuildConfig.ACCOUNT_TYPE.
@@ -203,9 +206,6 @@ android {
// Github API
buildConfigField "String", "GITHUB_CLIENT_ID", "\"c942cd81844d94e7e41b\""
buildConfigField "String", "GITHUB_CLIENT_SECRET", "\"f1dd17e70a0614abbd9310b00a310e23c6c8edff\""
-
- // Enable code coverage (Jacoco)
- testCoverageEnabled true
}
}
@@ -267,10 +267,11 @@ android {
dexOptions {
incremental = true
- // Disable preDexing, causes com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000) on some systems
- preDexLibraries = false
+ preDexLibraries = true
+ // dexInProcess requires much RAM, which is not available on all dev systems
+ dexInProcess = false
jumboMode = true
- javaMaxHeapSize "2g"
+ javaMaxHeapSize "4g"
}
packagingOptions {
@@ -284,8 +285,6 @@ android {
}
}
-// apply plugin: 'spoon'
-
task jacocoTestReport(type:JacocoReport, dependsOn: "testFdroidDebugUnitTest") {
group = "Reporting"
description = "Generate Jacoco coverage reports"
diff --git a/OpenKeychain/proguard-rules.pro b/OpenKeychain/proguard-rules.pro
index e37fe5af2..59e13843c 100644
--- a/OpenKeychain/proguard-rules.pro
+++ b/OpenKeychain/proguard-rules.pro
@@ -1,28 +1,21 @@
-# Add project specific ProGuard rules here.
-# You can edit the include path and order by changing the proguardFiles
-# directive in build.gradle.
-#
-# For more details, see
+# Documentation for ProGuard:
# http://developer.android.com/guide/developing/tools/proguard.html
+# http://proguard.sourceforge.net/
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-
-# Workaround for Samsung Android 4.2 bug
-# https://code.google.com/p/android/issues/detail?id=78377
-# https://code.google.com/p/android/issues/detail?id=78377#c188
-# https://code.google.com/p/android/issues/detail?id=78377#c302
--keepattributes **
--keep class !android.support.v7.view.menu.**,** {*;}
--dontpreverify
+#-dontshrink # shrinking enabled, see below
+#-dontobfuscate # obfuscation enabled for one class (see below)
-dontoptimize
--dontshrink
+-dontpreverify
+-keepattributes **
-dontwarn **
--dontnote ** \ No newline at end of file
+-dontnote **
+
+# Rules are defined as negation filters!
+# (! = negation filter, ** = all subpackages)
+# Keep everything (** {*;}) except...
+
+# * Obfuscate android.support.v7.view.menu.** to fix Samsung Android 4.2 bug
+# https://code.google.com/p/android/issues/detail?id=78377
+# * Remove unneeded Bouncy Castle packages to be under 64K limit
+# http://developer.android.com/tools/building/multidex.html
+-keep class !android.support.v7.view.menu.**,!org.bouncycastle.crypto.tls.**,!org.bouncycastle.pqc.**,!org.bouncycastle.x509.**,** {*;}
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index 8fa5f52c4..7247077d9 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -43,6 +43,11 @@
android:name="android.hardware.screen.portrait"
android:required="false" />
+ <!-- For OTG tokens -->
+ <uses-feature
+ android:name="android.hardware.usb.host"
+ android:required="false" />
+
<!-- TemporaryStorageProvider should be writable by OpenKeychain only, thus signature-level permission -->
<permission
android:name="${applicationId}.WRITE_TEMPORARY_STORAGE"
@@ -76,6 +81,7 @@
<!-- other group (for free) -->
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
@@ -89,6 +95,15 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Keychain.Light">
+ <!-- broadcast receiver for Wi-Fi Connection -->
+ <receiver
+ android:name=".receiver.NetworkReceiver"
+ android:enabled="false"
+ android:exported="true" >
+ <intent-filter>
+ <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
+ </intent-filter>
+ </receiver>
<!-- singleTop for NFC dispatch, see SecurityTokenOperationActivity -->
<activity
android:name=".ui.MainActivity"
@@ -870,6 +885,24 @@
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_import_keys" />
+ <!-- Usb interceptor activity -->
+ <activity
+ android:name=".ui.UsbEventReceiverActivity"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.Keychain.Transparent"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:taskAffinity="com.example.taskAffinityUsbEventReceiver"
+ android:process=":UsbEventReceiverActivityProcess"
+ android:exported="false">
+
+ <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+ android:resource="@xml/usb_device_filter" />
+ <intent-filter>
+ <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+ </intent-filter>
+ </activity>
+
<!-- DEPRECATED service,
using this service may lead to truncated data being returned to the caller -->
<service
@@ -951,4 +984,4 @@
</application>
-</manifest> \ No newline at end of file
+</manifest>
diff --git a/OpenKeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java b/OpenKeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java
index 703af94f4..7679f8486 100644
--- a/OpenKeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java
+++ b/OpenKeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java
@@ -6,15 +6,16 @@
package org.bouncycastle.openpgp.operator.jcajce;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.operator.PGPDataDecryptor;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
-import java.nio.ByteBuffer;
-import java.util.Map;
-
public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactory
{
private final PublicKeyDataDecryptorFactory mWrappedDecryptor;
@@ -59,6 +60,10 @@ public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactor
return mSessionKeyCache.get(bi);
}
+ if (mWrappedDecryptor == null) {
+ throw new IllegalStateException("tried to decrypt without wrapped decryptor, this is a bug!");
+ }
+
byte[] sessionData = mWrappedDecryptor.recoverSessionData(keyAlgorithm, secKeyData);
mSessionKeyCache.put(bi, sessionData);
return sessionData;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 53fb5afc6..fd6e903fa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -118,6 +118,7 @@ public final class Constants {
// keyserver sync settings
public static final String SYNC_CONTACTS = "syncContacts";
public static final String SYNC_KEYSERVER = "syncKeyserver";
+ public static final String ENABLE_WIFI_SYNC_ONLY = "enableWifiSyncOnly";
// other settings
public static final String EXPERIMENTAL_ENABLE_WORD_CONFIRM = "experimentalEnableWordConfirm";
public static final String EXPERIMENTAL_ENABLE_LINKED_IDENTITIES = "experimentalEnableLinkedIdentities";
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index e1f61a5ef..2f0ebe904 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -100,6 +100,11 @@ public class KeychainApplication extends Application {
// Add OpenKeychain account to Android to link contacts with keys and keyserver sync
createAccountIfNecessary(this);
+ if (Preferences.getKeyserverSyncEnabled(this)) {
+ // will update a keyserver sync if the interval has changed
+ KeyserverSyncAdapterService.enableKeyserverSync(this);
+ }
+
// if first time, enable keyserver and contact sync
if (Preferences.getPreferences(this).isFirstTime()) {
KeyserverSyncAdapterService.enableKeyserverSync(this);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java
index faa2a1848..6217d1a01 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java
@@ -23,10 +23,10 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import com.squareup.okhttp.OkHttpClient;
-import com.squareup.okhttp.Request;
-import com.squareup.okhttp.Response;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
@@ -34,6 +34,8 @@ import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.OkHttpClientFactory;
+import org.sufficientlysecure.keychain.util.TlsHelper;
import java.io.IOException;
import java.net.Proxy;
@@ -104,11 +106,10 @@ public class FacebookKeyserver extends Keyserver {
String request = String.format(FB_KEY_URL_FORMAT, fbUsername);
Log.d(Constants.TAG, "fetching from Facebook with: " + request + " proxy: " + mProxy);
- OkHttpClient client = new OkHttpClient();
- client.setProxy(mProxy);
-
URL url = new URL(request);
+ OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(url, mProxy);
+
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
// contains body both in case of success or failure
@@ -126,6 +127,9 @@ public class FacebookKeyserver extends Keyserver {
throw new QueryFailedException("Cannot connect to Facebook. "
+ "Check your Internet connection!"
+ (mProxy == Proxy.NO_PROXY ? "" : " Using proxy " + mProxy));
+ } catch (TlsHelper.TlsHelperException e) {
+ Log.e(Constants.TAG, "Exception in cert pinning", e);
+ throw new QueryFailedException("Exception in cert pinning. ");
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
index c2190318b..5e3d2ebc6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
@@ -18,16 +18,18 @@
package org.sufficientlysecure.keychain.keyimport;
-import com.squareup.okhttp.MediaType;
-import com.squareup.okhttp.OkHttpClient;
-import com.squareup.okhttp.Request;
-import com.squareup.okhttp.RequestBody;
-import com.squareup.okhttp.Response;
+
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.OkHttpClientFactory;
import org.sufficientlysecure.keychain.util.TlsHelper;
import java.io.IOException;
@@ -42,7 +44,6 @@ import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -199,43 +200,12 @@ public class HkpKeyserver extends Keyserver {
return mSecure ? "https://" : "http://";
}
- /**
- * returns a client with pinned certificate if necessary
- *
- * @param url url to be queried by client
- * @param proxy proxy to be used by client
- * @return client with a pinned certificate if necessary
- */
- public static OkHttpClient getClient(URL url, Proxy proxy) throws IOException {
- OkHttpClient client = new OkHttpClient();
-
- try {
- TlsHelper.usePinnedCertificateIfAvailable(client, url);
- } catch (TlsHelper.TlsHelperException e) {
- Log.w(Constants.TAG, e);
- }
-
- // don't follow any redirects
- client.setFollowRedirects(false);
- client.setFollowSslRedirects(false);
-
- if (proxy != null) {
- client.setProxy(proxy);
- client.setConnectTimeout(30000, TimeUnit.MILLISECONDS);
- } else {
- client.setProxy(Proxy.NO_PROXY);
- client.setConnectTimeout(5000, TimeUnit.MILLISECONDS);
- }
- client.setReadTimeout(45000, TimeUnit.MILLISECONDS);
-
- return client;
- }
private String query(String request, @NonNull Proxy proxy) throws QueryFailedException, HttpError {
try {
URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + request);
Log.d(Constants.TAG, "hkp keyserver query: " + url + " Proxy: " + proxy);
- OkHttpClient client = getClient(url, proxy);
+ OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(url, proxy);
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
String responseBody = response.body().string(); // contains body both in case of success or failure
@@ -249,6 +219,9 @@ public class HkpKeyserver extends Keyserver {
Log.e(Constants.TAG, "IOException at HkpKeyserver", e);
throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!" +
(proxy == Proxy.NO_PROXY ? "" : " Using proxy " + proxy));
+ } catch (TlsHelper.TlsHelperException e) {
+ Log.e(Constants.TAG, "Exception in pinning certs", e);
+ throw new QueryFailedException("Exception in pinning certs");
}
}
@@ -413,6 +386,7 @@ public class HkpKeyserver extends Keyserver {
Log.d(Constants.TAG, "hkp keyserver add: " + url);
Log.d(Constants.TAG, "params: " + params);
+
RequestBody body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), params);
Request request = new Request.Builder()
@@ -422,7 +396,7 @@ public class HkpKeyserver extends Keyserver {
.post(body)
.build();
- Response response = getClient(url, mProxy).newCall(request).execute();
+ Response response = OkHttpClientFactory.getClientPinnedIfAvailable(url, mProxy).newCall(request).execute();
Log.d(Constants.TAG, "response code: " + response.code());
Log.d(Constants.TAG, "answer: " + response.body().string());
@@ -434,6 +408,9 @@ public class HkpKeyserver extends Keyserver {
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
throw new AddKeyException();
+ } catch (TlsHelper.TlsHelperException e) {
+ Log.e(Constants.TAG, "Exception in pinning certs", e);
+ throw new AddKeyException();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java
index e5a128e32..a5f882dd0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java
@@ -2,12 +2,10 @@ package org.sufficientlysecure.keychain.linked;
import android.content.Context;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.BasicHttpParams;
+import okhttp3.CertificatePinner;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
import org.json.JSONException;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.linked.resources.GenericHttpsResource;
@@ -18,12 +16,9 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
-import org.thoughtcrime.ssl.pinning.util.PinningHelper;
+import org.sufficientlysecure.keychain.util.OkHttpClientFactory;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.HashMap;
@@ -233,46 +228,38 @@ public abstract class LinkedTokenResource extends LinkedResource {
}
- @SuppressWarnings("deprecation") // HttpRequestBase is deprecated
- public static String getResponseBody(Context context, HttpRequestBase request)
- throws IOException, HttpStatusException {
- return getResponseBody(context, request, null);
- }
- @SuppressWarnings("deprecation") // HttpRequestBase is deprecated
- public static String getResponseBody(Context context, HttpRequestBase request, String[] pins)
- throws IOException, HttpStatusException {
- StringBuilder sb = new StringBuilder();
+ private static CertificatePinner getCertificatePinner(String hostname, String[] pins){
+ CertificatePinner.Builder builder = new CertificatePinner.Builder();
+ for(String pin : pins){
+ builder.add(hostname,pin);
+ }
+ return builder.build();
+ }
- request.setHeader("User-Agent", "Open Keychain");
+ public static String getResponseBody(Request request, String... pins)
+ throws IOException, HttpStatusException {
- HttpClient httpClient;
- if (pins == null) {
- httpClient = new DefaultHttpClient(new BasicHttpParams());
+ Log.d("Connection to: " + request.url().url().getHost(), "");
+ OkHttpClient client;
+ if (pins != null) {
+ client = OkHttpClientFactory.getSimpleClientPinned(getCertificatePinner(request.url().url().getHost(), pins));
} else {
- httpClient = PinningHelper.getPinnedHttpClient(context, pins);
+ client = OkHttpClientFactory.getSimpleClient();
}
- HttpResponse response = httpClient.execute(request);
- int statusCode = response.getStatusLine().getStatusCode();
- String reason = response.getStatusLine().getReasonPhrase();
+ Response response = client.newCall(request).execute();
- if (statusCode != 200) {
- throw new HttpStatusException(statusCode, reason);
- }
- HttpEntity entity = response.getEntity();
- InputStream inputStream = entity.getContent();
+ int statusCode = response.code();
+ String reason = response.message();
- BufferedReader bReader = new BufferedReader(
- new InputStreamReader(inputStream, "UTF-8"), 8);
- String line;
- while ((line = bReader.readLine()) != null) {
- sb.append(line);
+ if (statusCode != 200) {
+ throw new HttpStatusException(statusCode, reason);
}
- return sb.toString();
+ return response.body().string();
}
public static class HttpStatusException extends Throwable {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java
index 82240c405..da531e8fa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java
@@ -6,7 +6,7 @@ import android.net.Uri;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
-import org.apache.http.client.methods.HttpGet;
+import okhttp3.Request;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@@ -32,14 +32,16 @@ public class GenericHttpsResource extends LinkedTokenResource {
token, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24));
}
- @SuppressWarnings("deprecation") // HttpGet is deprecated
@Override
protected String fetchResource (Context context, OperationLog log, int indent)
throws HttpStatusException, IOException {
log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString());
- HttpGet httpGet = new HttpGet(mSubUri);
- return getResponseBody(context, httpGet);
+ Request request = new Request.Builder()
+ .url(mSubUri.toURL())
+ .addHeader("User-Agent", "OpenKeychain")
+ .build();
+ return getResponseBody(request);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java
index 7a97ffd96..0e87ca6e5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java
@@ -6,7 +6,7 @@ import android.net.Uri;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
-import org.apache.http.client.methods.HttpGet;
+import okhttp3.Request;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -47,7 +47,7 @@ public class GithubResource extends LinkedTokenResource {
return String.format(context.getResources().getString(R.string.linked_id_github_text), token);
}
- @SuppressWarnings("deprecation") // HttpGet is deprecated
+
@Override
protected String fetchResource (Context context, OperationLog log, int indent)
throws HttpStatusException, IOException, JSONException {
@@ -55,8 +55,11 @@ public class GithubResource extends LinkedTokenResource {
log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString());
indent += 1;
- HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId);
- String response = getResponseBody(context, httpGet);
+ Request request = new Request.Builder()
+ .url("https://api.github.com/gists/" + mGistId)
+ .addHeader("User-Agent", "OpenKeychain")
+ .build();
+ String response = getResponseBody(request);
JSONObject obj = new JSONObject(response);
@@ -79,7 +82,7 @@ public class GithubResource extends LinkedTokenResource {
}
- @Deprecated // not used for now, but could be used to pick up earlier posted gist if already present?
+
@SuppressWarnings({ "deprecation", "unused" })
public static GithubResource searchInGithubStream(
Context context, String screenName, String needle, OperationLog log) {
@@ -94,12 +97,12 @@ public class GithubResource extends LinkedTokenResource {
try {
JSONArray array; {
- HttpGet httpGet =
- new HttpGet("https://api.github.com/users/" + screenName + "/gists");
- httpGet.setHeader("Content-Type", "application/json");
- httpGet.setHeader("User-Agent", "OpenKeychain");
-
- String response = getResponseBody(context, httpGet);
+ Request request = new Request.Builder()
+ .url("https://api.github.com/users/" + screenName + "/gists")
+ .addHeader("Content-Type", "application/json")
+ .addHeader("User-Agent", "OpenKeychain")
+ .build();
+ String response = getResponseBody(request);
array = new JSONArray(response);
}
@@ -116,10 +119,13 @@ public class GithubResource extends LinkedTokenResource {
continue;
}
String id = obj.getString("id");
- HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + id);
- httpGet.setHeader("User-Agent", "OpenKeychain");
- JSONObject gistObj = new JSONObject(getResponseBody(context, httpGet));
+ Request request = new Request.Builder()
+ .url("https://api.github.com/gists/" + id)
+ .addHeader("User-Agent", "OpenKeychain")
+ .build();
+
+ JSONObject gistObj = new JSONObject(getResponseBody(request));
JSONObject gistFiles = gistObj.getJSONObject("files");
Iterator<String> gistIt = gistFiles.keys();
if (!gistIt.hasNext()) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java
index 73e3d3643..db3b64225 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java
@@ -9,9 +9,9 @@ import android.util.Log;
import com.textuality.keybase.lib.JWalk;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.StringEntity;
+import okhttp3.MediaType;
+import okhttp3.Request;
+import okhttp3.RequestBody;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -84,18 +84,19 @@ public class TwitterResource extends LinkedTokenResource {
return null;
}
- HttpGet httpGet =
- new HttpGet("https://api.twitter.com/1.1/statuses/show.json"
- + "?id=" + mTweetId
- + "&include_entities=false");
-
// construct a normal HTTPS request and include an Authorization
// header with the value of Bearer <>
- httpGet.setHeader("Authorization", "Bearer " + authToken);
- httpGet.setHeader("Content-Type", "application/json");
+ Request request = new Request.Builder()
+ .url("https://api.twitter.com/1.1/statuses/show.json"
+ + "?id=" + mTweetId
+ + "&include_entities=false")
+ .addHeader("Authorization", "Bearer " + authToken)
+ .addHeader("Content-Type", "application/json")
+ .addHeader("User-Agent", "OpenKeychain")
+ .build();
try {
- String response = getResponseBody(context, httpGet, CERT_PINS);
+ String response = getResponseBody(request, CERT_PINS);
JSONObject obj = new JSONObject(response);
JSONObject user = obj.getJSONObject("user");
if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) {
@@ -157,21 +158,20 @@ public class TwitterResource extends LinkedTokenResource {
return null;
}
- HttpGet httpGet =
- new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json"
+ Request request = new Request.Builder()
+ .url("https://api.twitter.com/1.1/statuses/user_timeline.json"
+ "?screen_name=" + screenName
+ "&count=15"
+ "&include_rts=false"
+ "&trim_user=true"
- + "&exclude_replies=true");
-
- // construct a normal HTTPS request and include an Authorization
- // header with the value of Bearer <>
- httpGet.setHeader("Authorization", "Bearer " + authToken);
- httpGet.setHeader("Content-Type", "application/json");
+ + "&exclude_replies=true")
+ .addHeader("Authorization", "Bearer " + authToken)
+ .addHeader("Content-Type", "application/json")
+ .addHeader("User-Agent", "OpenKeychain")
+ .build();
try {
- String response = getResponseBody(context, httpGet, CERT_PINS);
+ String response = getResponseBody(request, CERT_PINS);
JSONArray array = new JSONArray(response);
for (int i = 0; i < array.length(); i++) {
@@ -216,12 +216,20 @@ public class TwitterResource extends LinkedTokenResource {
String base64Encoded = rot13("D293FQqanH0jH29KIaWJER5DomqSGRE2Ewc1LJACn3cbD1c"
+ "Fq1bmqSAQAz5MI2cIHKOuo3cPoRAQI1OyqmIVFJS6LHMXq2g6MRLkIj") + "==";
+ RequestBody requestBody = RequestBody.create(
+ MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"),
+ "grant_type=client_credentials");
+
// Step 2: Obtain a bearer token
- HttpPost httpPost = new HttpPost("https://api.twitter.com/oauth2/token");
- httpPost.setHeader("Authorization", "Basic " + base64Encoded);
- httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
- httpPost.setEntity(new StringEntity("grant_type=client_credentials"));
- JSONObject rawAuthorization = new JSONObject(getResponseBody(context, httpPost, CERT_PINS));
+ Request request = new Request.Builder()
+ .url("https://api.twitter.com/oauth2/token")
+ .addHeader("Authorization", "Basic " + base64Encoded)
+ .addHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
+ .addHeader("User-Agent", "OpenKeychain")
+ .post(requestBody)
+ .build();
+
+ JSONObject rawAuthorization = new JSONObject(getResponseBody(request, CERT_PINS));
// Applications should verify that the value associated with the
// token_type key of the returned object is bearer
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
index 79b42ecc4..b4b27f7ab 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
@@ -47,7 +47,7 @@ import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
import org.sufficientlysecure.keychain.service.UploadKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenSignOperationsBuilder;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Passphrase;
@@ -144,7 +144,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
int certifyOk = 0, certifyError = 0, uploadOk = 0, uploadError = 0;
- NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder(
+ SecurityTokenSignOperationsBuilder allRequiredInput = new SecurityTokenSignOperationsBuilder(
cryptoInput.getSignatureTime(), masterKeyId, masterKeyId);
// Work through all requested certifications
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/InputDataOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/InputDataOperation.java
index 43fc11b84..6682cc6e7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/InputDataOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/InputDataOperation.java
@@ -119,8 +119,9 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> {
// inform the storage provider about the mime type for this uri
if (decryptResult.getDecryptionMetadata() != null) {
- TemporaryFileProvider.setMimeType(mContext, currentInputUri,
- decryptResult.getDecryptionMetadata().getMimeType());
+ OpenPgpMetadata meta = decryptResult.getDecryptionMetadata();
+ TemporaryFileProvider.setName(mContext, currentInputUri, meta.getFilename());
+ TemporaryFileProvider.setMimeType(mContext, currentInputUri, meta.getMimeType());
}
} else {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
index 2ca74063c..5bca372cb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
@@ -43,7 +43,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenSignOperationsBuilder;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.InputData;
@@ -80,7 +80,7 @@ public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
int total = inputBytes != null ? 1 : inputUris.size(), count = 0;
ArrayList<PgpSignEncryptResult> results = new ArrayList<>();
- NfcSignOperationsBuilder pendingInputBuilder = null;
+ SecurityTokenSignOperationsBuilder pendingInputBuilder = null;
// if signing subkey has not explicitly been set, get first usable subkey capable of signing
if (input.getSignatureMasterKeyId() != Constants.key.none
@@ -161,7 +161,7 @@ public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
return new SignEncryptResult(log, requiredInput, results, cryptoInput);
}
if (pendingInputBuilder == null) {
- pendingInputBuilder = new NfcSignOperationsBuilder(requiredInput.mSignatureTime,
+ pendingInputBuilder = new SecurityTokenSignOperationsBuilder(requiredInput.mSignatureTime,
input.getSignatureMasterKeyId(), input.getSignatureSubKeyId());
}
pendingInputBuilder.addAll(requiredInput);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java
index 1a8f10d4f..7c394fc1e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java
@@ -131,7 +131,8 @@ public class DeleteResult extends InputPendingResult {
else if (mFail == 0) {
str = activity.getString(R.string.delete_nothing);
} else {
- str = activity.getResources().getQuantityString(R.plurals.delete_fail, mFail);
+ str = activity.getResources().getQuantityString(
+ R.plurals.delete_fail, mFail, mFail);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index ec2fddbd0..a3979904c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -566,8 +566,6 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_ERROR_BAD_SECURITY_TOKEN_SIZE(LogLevel.ERROR, R.string.edit_key_error_bad_security_token_size),
MSG_MF_ERROR_BAD_SECURITY_TOKEN_STRIPPED(LogLevel.ERROR, R.string.edit_key_error_bad_security_token_stripped),
MSG_MF_MASTER (LogLevel.DEBUG, R.string.msg_mf_master),
- MSG_MF_NOTATION_PIN (LogLevel.DEBUG, R.string.msg_mf_notation_pin),
- MSG_MF_NOTATION_EMPTY (LogLevel.DEBUG, R.string.msg_mf_notation_empty),
MSG_MF_PASSPHRASE (LogLevel.INFO, R.string.msg_mf_passphrase),
MSG_MF_PIN (LogLevel.INFO, R.string.msg_mf_pin),
MSG_MF_ADMIN_PIN (LogLevel.INFO, R.string.msg_mf_admin_pin),
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
index 77977b691..d2384e679 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -61,6 +61,8 @@ public abstract class KeyRing {
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
+ private static final Pattern EMAIL_PATTERN = Pattern.compile("^.*@.*\\..*$");
+
/**
* Splits userId string into naming part, email part, and comment part
* <p/>
@@ -71,25 +73,38 @@ public abstract class KeyRing {
if (!TextUtils.isEmpty(userId)) {
final Matcher matcher = USER_ID_PATTERN.matcher(userId);
if (matcher.matches()) {
- return new UserId(matcher.group(1), matcher.group(3), matcher.group(2));
+ String name = matcher.group(1).isEmpty() ? null : matcher.group(1);
+ String comment = matcher.group(2);
+ String email = matcher.group(3);
+ if (comment == null && email == null && name != null && EMAIL_PATTERN.matcher(name).matches()) {
+ email = name;
+ name = null;
+ }
+ return new UserId(name, email, comment);
}
}
return new UserId(null, null, null);
}
/**
- * Returns a composed user id. Returns null if name is null!
+ * Returns a composed user id. Returns null if name, email and comment are empty.
*/
public static String createUserId(UserId userId) {
- String userIdString = userId.name; // consider name a required value
- if (userIdString != null && !TextUtils.isEmpty(userId.comment)) {
- userIdString += " (" + userId.comment + ")";
+ StringBuilder userIdBuilder = new StringBuilder();
+ if (!TextUtils.isEmpty(userId.name)) {
+ userIdBuilder.append(userId.comment);
}
- if (userIdString != null && !TextUtils.isEmpty(userId.email)) {
- userIdString += " <" + userId.email + ">";
+ if (!TextUtils.isEmpty(userId.comment)) {
+ userIdBuilder.append(" (");
+ userIdBuilder.append(userId.comment);
+ userIdBuilder.append(")");
}
-
- return userIdString;
+ if (!TextUtils.isEmpty(userId.email)) {
+ userIdBuilder.append(" <");
+ userIdBuilder.append(userId.email);
+ userIdBuilder.append(">");
+ }
+ return userIdBuilder.length() == 0 ? null : userIdBuilder.toString();
}
public static class UserId implements Serializable {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java
index c4525e5cd..31a3f91b6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java
@@ -26,6 +26,8 @@ public class OpenPgpDecryptionResultBuilder {
// builder
private boolean mInsecure = false;
private boolean mEncrypted = false;
+ private byte[] sessionKey;
+ private byte[] decryptedSessionKey;
public void setInsecure(boolean insecure) {
this.mInsecure = insecure;
@@ -36,24 +38,26 @@ public class OpenPgpDecryptionResultBuilder {
}
public OpenPgpDecryptionResult build() {
- OpenPgpDecryptionResult result = new OpenPgpDecryptionResult();
-
if (mInsecure) {
Log.d(Constants.TAG, "RESULT_INSECURE");
- result.setResult(OpenPgpDecryptionResult.RESULT_INSECURE);
- return result;
+ return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_INSECURE, sessionKey, decryptedSessionKey);
}
if (mEncrypted) {
Log.d(Constants.TAG, "RESULT_ENCRYPTED");
- result.setResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED);
- } else {
- Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED");
- result.setResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED);
+ return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED, sessionKey, decryptedSessionKey);
}
- return result;
+ Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED");
+ return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED);
}
+ public void setSessionKey(byte[] sessionKey, byte[] decryptedSessionKey) {
+ if ((sessionKey == null) != (decryptedSessionKey == null)) {
+ throw new AssertionError("sessionKey must be null iff decryptedSessionKey is null!");
+ }
+ this.sessionKey = sessionKey;
+ this.decryptedSessionKey = decryptedSessionKey;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java
index aa1c2e037..ae0a31191 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java
@@ -37,7 +37,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenSignOperationsBuilder;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
@@ -76,7 +76,7 @@ public class PgpCertifyOperation {
// get the master subkey (which we certify for)
PGPPublicKey publicKey = publicRing.getPublicKey().getPublicKey();
- NfcSignOperationsBuilder requiredInput = new NfcSignOperationsBuilder(creationTimestamp,
+ SecurityTokenSignOperationsBuilder requiredInput = new SecurityTokenSignOperationsBuilder(creationTimestamp,
publicKey.getKeyID(), publicKey.getKeyID());
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
index e15139a7f..a27e4a8d5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
@@ -26,9 +26,12 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
import java.security.SignatureException;
import java.util.Date;
import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
import android.content.Context;
import android.support.annotation.NonNull;
@@ -60,7 +63,6 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.key;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.BaseOperation;
-import org.sufficientlysecure.keychain.util.CharsetVerifier;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@@ -73,6 +75,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.CharsetVerifier;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -197,6 +200,10 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
PGPEncryptedData encryptedData;
InputStream cleartextStream;
+ // the cached session key
+ byte[] sessionKey;
+ byte[] decryptedSessionKey;
+
int symmetricEncryptionAlgo = 0;
boolean skippedDisallowedKey = false;
@@ -304,6 +311,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
// if this worked out so far, the data is encrypted
decryptionResultBuilder.setEncrypted(true);
+ if (esResult.sessionKey != null && esResult.decryptedSessionKey != null) {
+ decryptionResultBuilder.setSessionKey(esResult.sessionKey, esResult.decryptedSessionKey);
+ }
if (esResult.insecureEncryptionKey) {
log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
@@ -545,10 +555,14 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
boolean anyPacketFound = false;
+ boolean decryptedSessionKeyAvailable = false;
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
CanonicalizedSecretKey decryptionKey = null;
+ CachingDataDecryptorFactory cachedKeyDecryptorFactory = new CachingDataDecryptorFactory(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME, cryptoInput.getCryptoData());
+ ;
Passphrase passphrase = null;
@@ -569,6 +583,13 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
log.add(LogType.MSG_DC_ASYM, indent,
KeyFormattingUtils.convertKeyIdToHex(subKeyId));
+ decryptedSessionKeyAvailable = cachedKeyDecryptorFactory.hasCachedSessionData(encData);
+ if (decryptedSessionKeyAvailable) {
+ asymmetricPacketFound = true;
+ encryptedDataAsymmetric = encData;
+ break;
+ }
+
CachedPublicKeyRing cachedPublicKeyRing;
try {
// get actual keyring object based on master key id
@@ -746,34 +767,38 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
currentProgress += 2;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
- try {
- log.add(LogType.MSG_DC_UNLOCKING, indent + 1);
- if (!decryptionKey.unlock(passphrase)) {
- log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1);
+ CachingDataDecryptorFactory decryptorFactory;
+ if (decryptedSessionKeyAvailable) {
+ decryptorFactory = cachedKeyDecryptorFactory;
+ } else {
+ try {
+ log.add(LogType.MSG_DC_UNLOCKING, indent + 1);
+ if (!decryptionKey.unlock(passphrase)) {
+ log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1);
+ return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
+ }
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1);
return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
}
- } catch (PgpGeneralException e) {
- log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1);
- return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
- }
-
- currentProgress += 2;
- updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
- CachingDataDecryptorFactory decryptorFactory
- = decryptionKey.getCachingDecryptorFactory(cryptoInput);
+ currentProgress += 2;
+ updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
- // special case: if the decryptor does not have a session key cached for this encrypted
- // data, and can't actually decrypt on its own, return a pending intent
- if (!decryptorFactory.canDecrypt()
- && !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) {
+ decryptorFactory = decryptionKey.getCachingDecryptorFactory(cryptoInput);
- log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
- return result.with(new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
- decryptionKey.getRing().getMasterKeyId(),
- decryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
- ), cryptoInput));
+ // special case: if the decryptor does not have a session key cached for this encrypted
+ // data, and can't actually decrypt on its own, return a pending intent
+ if (!decryptorFactory.canDecrypt()
+ && !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) {
+ log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
+ return result.with(new DecryptVerifyResult(log,
+ RequiredInputParcel.createSecurityTokenDecryptOperation(
+ decryptionKey.getRing().getMasterKeyId(),
+ decryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
+ ), cryptoInput));
+ }
}
try {
@@ -786,8 +811,13 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
result.symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
result.encryptedData = encryptedDataAsymmetric;
- cryptoInput.addCryptoData(decryptorFactory.getCachedSessionKeys());
-
+ Map<ByteBuffer, byte[]> cachedSessionKeys = decryptorFactory.getCachedSessionKeys();
+ cryptoInput.addCryptoData(cachedSessionKeys);
+ if (cachedSessionKeys.size() >= 1) {
+ Entry<ByteBuffer, byte[]> entry = cachedSessionKeys.entrySet().iterator().next();
+ result.sessionKey = entry.getKey().array();
+ result.decryptedSessionKey = entry.getValue();
+ }
} else {
// there wasn't even any useful data
if (!anyPacketFound) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index e43548165..ce9c30894 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -18,6 +18,23 @@
package org.sufficientlysecure.keychain.pgp;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Stack;
+import java.util.concurrent.atomic.AtomicBoolean;
+
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.sig.Features;
@@ -57,13 +74,12 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcKeyToCardOperationsBuilder;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenKeyToCardOperationsBuilder;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenSignOperationsBuilder;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -71,22 +87,6 @@ import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Primes;
import org.sufficientlysecure.keychain.util.ProgressScaler;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.SignatureException;
-import java.security.spec.ECGenParameterSpec;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.Stack;
-import java.util.concurrent.atomic.AtomicBoolean;
-
/**
* This class is the single place where ALL operations that actually modify a PGP public or secret
* key take place.
@@ -496,10 +496,10 @@ public class PgpKeyOperation {
OperationLog log,
int indent) {
- NfcSignOperationsBuilder nfcSignOps = new NfcSignOperationsBuilder(
+ SecurityTokenSignOperationsBuilder nfcSignOps = new SecurityTokenSignOperationsBuilder(
cryptoInput.getSignatureTime(), masterSecretKey.getKeyID(),
masterSecretKey.getKeyID());
- NfcKeyToCardOperationsBuilder nfcKeyToCardOps = new NfcKeyToCardOperationsBuilder(
+ SecurityTokenKeyToCardOperationsBuilder nfcKeyToCardOps = new SecurityTokenKeyToCardOperationsBuilder(
masterSecretKey.getKeyID());
progress(R.string.progress_modify, 0);
@@ -1058,8 +1058,8 @@ public class PgpKeyOperation {
log.add(LogType.MSG_MF_PASSPHRASE, indent);
indent += 1;
- sKR = applyNewUnlock(sKR, masterPublicKey, masterPrivateKey,
- cryptoInput.getPassphrase(), saveParcel.mNewUnlock, log, indent);
+ sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(),
+ saveParcel.mNewUnlock.mNewPassphrase, log, indent);
if (sKR == null) {
// The error has been logged above, just return a bad state
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -1191,76 +1191,6 @@ public class PgpKeyOperation {
}
-
- private static PGPSecretKeyRing applyNewUnlock(
- PGPSecretKeyRing sKR,
- PGPPublicKey masterPublicKey,
- PGPPrivateKey masterPrivateKey,
- Passphrase passphrase,
- ChangeUnlockParcel newUnlock,
- OperationLog log, int indent) throws PGPException {
-
- if (newUnlock.mNewPassphrase != null) {
- sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPassphrase, log, indent);
-
- // if there is any old packet with notation data
- if (hasNotationData(sKR)) {
-
- log.add(LogType.MSG_MF_NOTATION_EMPTY, indent);
-
- // add packet with EMPTY notation data (updates old one, but will be stripped later)
- PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
- PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
- { // set subpackets
- PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- hashedPacketsGen.setExportable(false, false);
- sGen.setHashedSubpackets(hashedPacketsGen.generate());
- }
- sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
- PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
-
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
- sKR = PGPSecretKeyRing.insertSecretKey(sKR,
- PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey));
- }
-
- return sKR;
- }
-
- if (newUnlock.mNewPin != null) {
- sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPin, log, indent);
-
- log.add(LogType.MSG_MF_NOTATION_PIN, indent);
-
- // add packet with "pin" notation data
- PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
- PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
- { // set subpackets
- PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- hashedPacketsGen.setExportable(false, false);
- hashedPacketsGen.setNotationData(false, true, "unlock.pin@sufficientlysecure.org", "1");
- sGen.setHashedSubpackets(hashedPacketsGen.generate());
- }
- sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
- PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
-
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
- sKR = PGPSecretKeyRing.insertSecretKey(sKR,
- PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey));
-
- return sKR;
- }
-
- throw new UnsupportedOperationException("PIN passphrases not yet implemented!");
-
- }
-
/** This method returns true iff the provided keyring has a local direct key signature
* with notation data.
*/
@@ -1294,8 +1224,7 @@ public class PgpKeyOperation {
PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(newPassphrase.getCharArray());
- // noinspection unchecked
- for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
+ for (PGPSecretKey sKey : new IterableIterator<>(sKR.getSecretKeys())) {
log.add(LogType.MSG_MF_PASSPHRASE_KEY, indent,
KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID()));
@@ -1348,7 +1277,7 @@ public class PgpKeyOperation {
PGPPublicKey masterPublicKey,
int flags, long expiry,
CryptoInputParcel cryptoInput,
- NfcSignOperationsBuilder nfcSignOps,
+ SecurityTokenSignOperationsBuilder nfcSignOps,
int indent, OperationLog log)
throws PGPException, IOException, SignatureException {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
index 009876045..4830d5333 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
@@ -470,7 +470,13 @@ public class PgpSignEncryptOperation extends BaseOperation {
InputStream in = new BufferedInputStream(inputData.getInputStream());
if (enableCompression) {
- compressGen = new PGPCompressedDataGenerator(input.getCompressionAlgorithm());
+ // Use preferred compression algo
+ int algo = input.getCompressionAlgorithm();
+ if (algo == PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT) {
+ algo = PgpSecurityConstants.DEFAULT_COMPRESSION_ALGORITHM;
+ }
+
+ compressGen = new PGPCompressedDataGenerator(algo);
bcpgOut = new BCPGOutputStream(compressGen.open(out));
} else {
bcpgOut = new BCPGOutputStream(out);
@@ -514,7 +520,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
} catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) {
// this secret key diverts to a OpenPGP card, throw exception with hash that will be signed
log.add(LogType.MSG_PSE_PENDING_NFC, indent);
- return new PgpSignEncryptResult(log, RequiredInputParcel.createNfcSignOperation(
+ return new PgpSignEncryptResult(log, RequiredInputParcel.createSecurityTokenSignOperation(
signingKey.getRing().getMasterKeyId(), signingKey.getKeyId(),
e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()), cryptoInput);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index d696b9d70..b0db36b06 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -35,6 +35,8 @@ import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
+import android.support.annotation.VisibleForTesting;
+
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.SignatureSubpacketTags;
@@ -42,15 +44,22 @@ import org.bouncycastle.bcpg.UserAttributeSubpacketTags;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.openpgp.PGPKeyRing;
import org.bouncycastle.openpgp.PGPObjectFactory;
+import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureList;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@@ -1310,4 +1319,37 @@ public class UncachedKeyRing {
|| algorithm == PGPPublicKey.ECDH;
}
+ // ONLY TO BE USED FOR TESTING!!
+ @VisibleForTesting
+ public static UncachedKeyRing forTestingOnlyAddDummyLocalSignature(
+ UncachedKeyRing uncachedKeyRing, String passphrase) throws Exception {
+ PGPSecretKeyRing sKR = (PGPSecretKeyRing) uncachedKeyRing.mRing;
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ PGPPrivateKey masterPrivateKey = sKR.getSecretKey().extractPrivateKey(keyDecryptor);
+ PGPPublicKey masterPublicKey = uncachedKeyRing.mRing.getPublicKey();
+
+ // add packet with "pin" notation data
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
+ PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ { // set subpackets
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen.setExportable(false, false);
+ hashedPacketsGen.setNotationData(false, true, "dummynotationdata", "some data");
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ }
+ sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
+ PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
+
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR,
+ PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey));
+
+ return new UncachedKeyRing(sKR);
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 177f07344..90a695547 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -60,6 +60,9 @@ public class KeychainContract {
String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID
String TYPE = "type"; // not a database id
String USER_ID = "user_id"; // not a database id
+ String NAME = "name";
+ String EMAIL = "email";
+ String COMMENT = "comment";
String ATTRIBUTE_DATA = "attribute_data"; // not a database id
String RANK = "rank"; // ONLY used for sorting! no key, no nothing!
String IS_PRIMARY = "is_primary";
@@ -359,6 +362,9 @@ public class KeychainContract {
public static class Certs implements CertsColumns, BaseColumns {
public static final String USER_ID = UserPacketsColumns.USER_ID;
+ public static final String NAME = UserPacketsColumns.NAME;
+ public static final String EMAIL = UserPacketsColumns.EMAIL;
+ public static final String COMMENT = UserPacketsColumns.COMMENT;
public static final String SIGNER_UID = "signer_user_id";
public static final int UNVERIFIED = 0;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 752c13007..0eb7a0cdb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -54,7 +54,7 @@ import java.io.IOException;
*/
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db";
- private static final int DATABASE_VERSION = 14;
+ private static final int DATABASE_VERSION = 17;
static Boolean apgHack = false;
private Context mContext;
@@ -115,6 +115,9 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ UserPacketsColumns.MASTER_KEY_ID + " INTEGER, "
+ UserPacketsColumns.TYPE + " INT, "
+ UserPacketsColumns.USER_ID + " TEXT, "
+ + UserPacketsColumns.NAME + " TEXT, "
+ + UserPacketsColumns.EMAIL + " TEXT, "
+ + UserPacketsColumns.COMMENT + " TEXT, "
+ UserPacketsColumns.ATTRIBUTE_DATA + " BLOB, "
+ UserPacketsColumns.IS_PRIMARY + " INTEGER, "
@@ -276,37 +279,45 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE user_ids ADD COLUMN type INTEGER");
db.execSQL("ALTER TABLE user_ids ADD COLUMN attribute_data BLOB");
case 7:
- // consolidate
- case 8:
// new table for allowed key ids in API
try {
db.execSQL(CREATE_API_APPS_ALLOWED_KEYS);
} catch (Exception e) {
// never mind, the column probably already existed
}
- case 9:
+ case 8:
// tbale name for user_ids changed to user_packets
db.execSQL("DROP TABLE IF EXISTS certs");
db.execSQL("DROP TABLE IF EXISTS user_ids");
db.execSQL(CREATE_USER_PACKETS);
db.execSQL(CREATE_CERTS);
- case 10:
+ case 9:
// do nothing here, just consolidate
- case 11:
+ case 10:
// fix problems in database, see #1402 for details
// https://github.com/open-keychain/open-keychain/issues/1402
db.execSQL("DELETE FROM api_accounts WHERE key_id BETWEEN 0 AND 3");
- case 12:
+ case 11:
db.execSQL(CREATE_UPDATE_KEYS);
- case 13:
+ case 12:
// do nothing here, just consolidate
- case 14:
+ case 13:
db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ");");
db.execSQL("CREATE INDEX uids_by_rank ON user_packets (" + UserPacketsColumns.RANK + ", "
+ UserPacketsColumns.USER_ID + ", " + UserPacketsColumns.MASTER_KEY_ID + ");");
db.execSQL("CREATE INDEX verified_certs ON certs ("
+ CertsColumns.VERIFIED + ", " + CertsColumns.MASTER_KEY_ID + ");");
-
+ case 14:
+ db.execSQL("ALTER TABLE user_packets ADD COLUMN name TEXT");
+ db.execSQL("ALTER TABLE user_packets ADD COLUMN email TEXT");
+ db.execSQL("ALTER TABLE user_packets ADD COLUMN comment TEXT");
+ case 15:
+ db.execSQL("CREATE INDEX uids_by_name ON user_packets (name COLLATE NOCASE)");
+ db.execSQL("CREATE INDEX uids_by_email ON user_packets (email COLLATE NOCASE)");
+ if (oldVersion == 14) {
+ // no consolidate necessary
+ return;
+ }
}
// always do consolidate after upgrade
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index 0cb8e4675..8a5d09d7b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -308,13 +308,18 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM);
projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT);
projectionMap.put(KeyRings.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID);
+ projectionMap.put(KeyRings.NAME, Tables.USER_PACKETS + "." + UserPackets.NAME);
+ projectionMap.put(KeyRings.EMAIL, Tables.USER_PACKETS + "." + UserPackets.EMAIL);
+ projectionMap.put(KeyRings.COMMENT, Tables.USER_PACKETS + "." + UserPackets.COMMENT);
projectionMap.put(KeyRings.HAS_DUPLICATE_USER_ID,
- "(EXISTS (SELECT * FROM " + Tables.USER_PACKETS + " AS dups"
+ "(EXISTS (SELECT * FROM " + Tables.USER_PACKETS + " AS dups"
+ " WHERE dups." + UserPackets.MASTER_KEY_ID
+ " != " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND dups." + UserPackets.RANK + " = 0"
- + " AND dups." + UserPackets.USER_ID
- + " = "+ Tables.USER_PACKETS + "." + UserPackets.USER_ID
+ + " AND dups." + UserPackets.NAME
+ + " = " + Tables.USER_PACKETS + "." + UserPackets.NAME + " COLLATE NOCASE"
+ + " AND dups." + UserPackets.EMAIL
+ + " = " + Tables.USER_PACKETS + "." + UserPackets.EMAIL + " COLLATE NOCASE"
+ ")) AS " + KeyRings.HAS_DUPLICATE_USER_ID);
projectionMap.put(KeyRings.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
projectionMap.put(KeyRings.PUBKEY_DATA,
@@ -451,12 +456,12 @@ public class KeychainProvider extends ContentProvider {
if (i != 0) {
emailWhere += " OR ";
}
- emailWhere += "tmp." + UserPackets.USER_ID + " LIKE ";
- // match '*<email>', so it has to be at the *end* of the user id
if (match == KEY_RINGS_FIND_BY_EMAIL) {
- emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
+ emailWhere += "tmp." + UserPackets.EMAIL + " LIKE "
+ + DatabaseUtils.sqlEscapeString(chunks[i]);
} else {
- emailWhere += DatabaseUtils.sqlEscapeString("%" + chunks[i] + "%");
+ emailWhere += "tmp." + UserPackets.USER_ID + " LIKE "
+ + DatabaseUtils.sqlEscapeString("%" + chunks[i] + "%");
}
gotCondition = true;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 72a3e2ff5..a0ebc691d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -19,17 +19,6 @@
package org.sufficientlysecure.keychain.provider;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -79,6 +68,17 @@ import org.sufficientlysecure.keychain.util.ProgressFixedScaler;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.Utf8Util;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
/**
* This class contains high level methods for database access. Despite its
* name, it is not only a helper but actually the main interface for all
@@ -452,11 +452,13 @@ public class ProviderHelper {
mIndent += 1;
for (byte[] rawUserId : masterKey.getUnorderedRawUserIds()) {
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
-
UserPacketItem item = new UserPacketItem();
uids.add(item);
+ KeyRing.UserId splitUserId = KeyRing.splitUserId(userId);
item.userId = userId;
-
+ item.name = splitUserId.name;
+ item.email = splitUserId.email;
+ item.comment = splitUserId.comment;
int unknownCerts = 0;
log(LogType.MSG_IP_UID_PROCESSING, userId);
@@ -746,6 +748,9 @@ public class ProviderHelper {
private static class UserPacketItem implements Comparable<UserPacketItem> {
Integer type;
String userId;
+ String name;
+ String email;
+ String comment;
byte[] attributeData;
boolean isPrimary = false;
WrappedSignature selfCert;
@@ -1437,6 +1442,9 @@ public class ProviderHelper {
values.put(UserPackets.MASTER_KEY_ID, masterKeyId);
values.put(UserPackets.TYPE, item.type);
values.put(UserPackets.USER_ID, item.userId);
+ values.put(UserPackets.NAME, item.name);
+ values.put(UserPackets.EMAIL, item.email);
+ values.put(UserPackets.COMMENT, item.comment);
values.put(UserPackets.ATTRIBUTE_DATA, item.attributeData);
values.put(UserPackets.IS_PRIMARY, item.isPrimary);
values.put(UserPackets.IS_REVOKED, item.selfRevocation != null);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryFileProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryFileProvider.java
index 68963d595..bb44314d7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryFileProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryFileProvider.java
@@ -99,6 +99,12 @@ public class TemporaryFileProvider extends ContentProvider {
return context.getContentResolver().insert(CONTENT_URI, contentValues);
}
+ public static int setName(Context context, Uri uri, String name) {
+ ContentValues values = new ContentValues();
+ values.put(TemporaryFileColumns.COLUMN_NAME, name);
+ return context.getContentResolver().update(uri, values, null, null);
+ }
+
public static int setMimeType(Context context, Uri uri, String mimetype) {
ContentValues values = new ContentValues();
values.put(TemporaryFileColumns.COLUMN_TYPE, mimetype);
@@ -283,8 +289,11 @@ public class TemporaryFileProvider extends ContentProvider {
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- if (values.size() != 1 || !values.containsKey(TemporaryFileColumns.COLUMN_TYPE)) {
- throw new UnsupportedOperationException("Update supported only for type field!");
+ if (values.size() != 1) {
+ throw new UnsupportedOperationException("Update supported only for one field at a time!");
+ }
+ if (!values.containsKey(TemporaryFileColumns.COLUMN_NAME) && !values.containsKey(TemporaryFileColumns.COLUMN_TYPE)) {
+ throw new UnsupportedOperationException("Update supported only for name and type field!");
}
if (selection != null || selectionArgs != null) {
throw new UnsupportedOperationException("Update supported only for plain uri!");
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/receiver/NetworkReceiver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/receiver/NetworkReceiver.java
new file mode 100644
index 000000000..7c103a9a3
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/receiver/NetworkReceiver.java
@@ -0,0 +1,52 @@
+package org.sufficientlysecure.keychain.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class NetworkReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ ConnectivityManager conn = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = conn.getActiveNetworkInfo();
+ boolean isTypeWifi = (networkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+ boolean isConnected = networkInfo.isConnected();
+
+ if (isTypeWifi && isConnected) {
+
+ // broadcaster receiver disabled
+ setWifiReceiverComponent(false, context);
+ Intent serviceIntent = new Intent(context, KeyserverSyncAdapterService.class);
+ serviceIntent.setAction(KeyserverSyncAdapterService.ACTION_SYNC_NOW);
+ context.startService(serviceIntent);
+ }
+ }
+
+ public void setWifiReceiverComponent(Boolean isEnabled, Context context) {
+
+ PackageManager pm = context.getPackageManager();
+ ComponentName compName = new ComponentName(context,
+ NetworkReceiver.class);
+
+ if (isEnabled) {
+ pm.setComponentEnabledSetting(compName,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
+ Log.d(Constants.TAG, "Wifi Receiver is enabled!");
+ } else {
+ pm.setComponentEnabledSetting(compName,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
+ Log.d(Constants.TAG, "Wifi Receiver is disabled!");
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java
index 690a4d1a2..001fa9dc8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java
@@ -50,10 +50,10 @@ public class ApiPendingIntentFactory {
CryptoInputParcel cryptoInput) {
switch (requiredInput.mType) {
- case NFC_MOVE_KEY_TO_CARD:
- case NFC_DECRYPT:
- case NFC_SIGN: {
- return createNfcOperationPendingIntent(data, requiredInput, cryptoInput);
+ case SECURITY_TOKEN_MOVE_KEY_TO_CARD:
+ case SECURITY_TOKEN_DECRYPT:
+ case SECURITY_TOKEN_SIGN: {
+ return createSecurityTokenOperationPendingIntent(data, requiredInput, cryptoInput);
}
case PASSPHRASE: {
@@ -65,7 +65,7 @@ public class ApiPendingIntentFactory {
}
}
- private PendingIntent createNfcOperationPendingIntent(Intent data, RequiredInputParcel requiredInput, CryptoInputParcel cryptoInput) {
+ private PendingIntent createSecurityTokenOperationPendingIntent(Intent data, RequiredInputParcel requiredInput, CryptoInputParcel cryptoInput) {
Intent intent = new Intent(mContext, RemoteSecurityTokenOperationActivity.class);
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(RemoteSecurityTokenOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 2d70f3681..e17310b65 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -35,6 +35,7 @@ import android.app.Service;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -103,20 +104,20 @@ public class OpenPgpService extends Service {
}
private static class KeyIdResult {
- final Intent mRequiredUserInteraction;
+ final Intent mResultIntent;
final HashSet<Long> mKeyIds;
- KeyIdResult(Intent requiredUserInteraction) {
- mRequiredUserInteraction = requiredUserInteraction;
+ KeyIdResult(Intent resultIntent) {
+ mResultIntent = resultIntent;
mKeyIds = null;
}
KeyIdResult(HashSet<Long> keyIds) {
- mRequiredUserInteraction = null;
+ mResultIntent = null;
mKeyIds = keyIds;
}
}
- private KeyIdResult returnKeyIdsFromEmails(Intent data, String[] encryptionUserIds) {
+ private KeyIdResult returnKeyIdsFromEmails(Intent data, String[] encryptionUserIds, boolean isOpportunistic) {
boolean noUserIdsCheck = (encryptionUserIds == null || encryptionUserIds.length == 0);
boolean missingUserIdsCheck = false;
boolean duplicateUserIdsCheck = false;
@@ -159,9 +160,15 @@ public class OpenPgpService extends Service {
}
}
- if (noUserIdsCheck || missingUserIdsCheck || duplicateUserIdsCheck) {
- // allow the user to verify pub key selection
+ if (isOpportunistic && (noUserIdsCheck || missingUserIdsCheck)) {
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_ERROR,
+ new OpenPgpError(OpenPgpError.OPPORTUNISTIC_MISSING_KEYS, "missing keys in opportunistic mode"));
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
+ return new KeyIdResult(result);
+ }
+ if (noUserIdsCheck || missingUserIdsCheck || duplicateUserIdsCheck) {
// convert ArrayList<Long> to long[]
long[] keyIdsArray = getUnboxedLongArray(keyIds);
ApiPendingIntentFactory piFactory = new ApiPendingIntentFactory(getBaseContext());
@@ -173,15 +180,14 @@ public class OpenPgpService extends Service {
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
return new KeyIdResult(result);
- } else {
- // everything was easy, we have exactly one key for every email
-
- if (keyIds.isEmpty()) {
- Log.e(Constants.TAG, "keyIdsArray.length == 0, should never happen!");
- }
+ }
- return new KeyIdResult(keyIds);
+ // everything was easy, we have exactly one key for every email
+ if (keyIds.isEmpty()) {
+ Log.e(Constants.TAG, "keyIdsArray.length == 0, should never happen!");
}
+
+ return new KeyIdResult(keyIds);
}
private Intent signImpl(Intent data, InputStream inputStream,
@@ -302,12 +308,12 @@ public class OpenPgpService extends Service {
// get key ids based on given user ids
if (data.hasExtra(OpenPgpApi.EXTRA_USER_IDS)) {
String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS);
- data.removeExtra(OpenPgpApi.EXTRA_USER_IDS);
+ boolean isOpportunistic = data.getBooleanExtra(OpenPgpApi.EXTRA_OPPORTUNISTIC_ENCRYPTION, false);
// give params through to activity...
- KeyIdResult result = returnKeyIdsFromEmails(data, userIds);
+ KeyIdResult result = returnKeyIdsFromEmails(data, userIds, isOpportunistic);
- if (result.mRequiredUserInteraction != null) {
- return result.mRequiredUserInteraction;
+ if (result.mResultIntent != null) {
+ return result.mResultIntent;
}
encryptKeyIds.addAll(result.mKeyIds);
}
@@ -448,6 +454,14 @@ public class OpenPgpService extends Service {
cryptoInput.mPassphrase =
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
+ if (data.hasExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT_WRAPPER)) {
+ Bundle wrapperBundle = data.getBundleExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT_WRAPPER);
+ wrapperBundle.setClassLoader(getClassLoader());
+ OpenPgpDecryptionResult decryptionResult = wrapperBundle.getParcelable(OpenPgpApi.EXTRA_DECRYPTION_RESULT);
+ if (decryptionResult != null && decryptionResult.hasDecryptedSessionKey()) {
+ cryptoInput.addCryptoData(decryptionResult.sessionKey, decryptionResult.decryptedSessionKey);
+ }
+ }
byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE);
@@ -695,10 +709,9 @@ public class OpenPgpService extends Service {
} else {
// get key ids based on given user ids
String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS);
- data.removeExtra(OpenPgpApi.EXTRA_USER_IDS);
- KeyIdResult keyResult = returnKeyIdsFromEmails(data, userIds);
- if (keyResult.mRequiredUserInteraction != null) {
- return keyResult.mRequiredUserInteraction;
+ KeyIdResult keyResult = returnKeyIdsFromEmails(data, userIds, false);
+ if (keyResult.mResultIntent != null) {
+ return keyResult.mResultIntent;
}
if (keyResult.mKeyIds == null) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/CardException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/CardException.java
new file mode 100644
index 000000000..905deca90
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/CardException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import java.io.IOException;
+
+public class CardException extends IOException {
+ private short mResponseCode;
+
+ public CardException(String detailMessage, short responseCode) {
+ super(detailMessage);
+ mResponseCode = responseCode;
+ }
+
+ public short getResponseCode() {
+ return mResponseCode;
+ }
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyType.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyType.java
new file mode 100644
index 000000000..0e28f022b
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyType.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+
+public enum KeyType {
+ SIGN(0, 0xB6, 0xCE, 0xC7),
+ ENCRYPT(1, 0xB8, 0xCF, 0xC8),
+ AUTH(2, 0xA4, 0xD0, 0xC9),;
+
+ private final int mIdx;
+ private final int mSlot;
+ private final int mTimestampObjectId;
+ private final int mFingerprintObjectId;
+
+ KeyType(final int idx, final int slot, final int timestampObjectId, final int fingerprintObjectId) {
+ this.mIdx = idx;
+ this.mSlot = slot;
+ this.mTimestampObjectId = timestampObjectId;
+ this.mFingerprintObjectId = fingerprintObjectId;
+ }
+
+ public static KeyType from(final CanonicalizedSecretKey key) {
+ if (key.canSign() || key.canCertify()) {
+ return SIGN;
+ } else if (key.canEncrypt()) {
+ return ENCRYPT;
+ } else if (key.canAuthenticate()) {
+ return AUTH;
+ }
+ return null;
+ }
+
+ public int getIdx() {
+ return mIdx;
+ }
+
+ public int getmSlot() {
+ return mSlot;
+ }
+
+ public int getTimestampObjectId() {
+ return mTimestampObjectId;
+ }
+
+ public int getmFingerprintObjectId() {
+ return mFingerprintObjectId;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java
new file mode 100644
index 000000000..3b2dd838d
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import android.nfc.Tag;
+
+import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenNfcActivity;
+
+import java.io.IOException;
+
+import nordpol.IsoCard;
+import nordpol.android.AndroidCard;
+
+public class NfcTransport implements Transport {
+ // timeout is set to 100 seconds to avoid cancellation during calculation
+ private static final int TIMEOUT = 100 * 1000;
+ private final Tag mTag;
+ private IsoCard mIsoCard;
+
+ public NfcTransport(Tag tag) {
+ this.mTag = tag;
+ }
+
+ /**
+ * Transmit and receive data
+ * @param data data to transmit
+ * @return received data
+ * @throws IOException
+ */
+ @Override
+ public byte[] transceive(final byte[] data) throws IOException {
+ return mIsoCard.transceive(data);
+ }
+
+ /**
+ * Disconnect and release connection
+ */
+ @Override
+ public void release() {
+ // Not supported
+ }
+
+ @Override
+ public boolean isConnected() {
+ return mIsoCard != null && mIsoCard.isConnected();
+ }
+
+ /**
+ * Check if Transport supports persistent connections e.g connections which can
+ * handle multiple operations in one session
+ * @return true if transport supports persistent connections
+ */
+ @Override
+ public boolean isPersistentConnectionAllowed() {
+ return false;
+ }
+
+ /**
+ * Connect to NFC device.
+ * <p/>
+ * On general communication, see also
+ * http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx
+ * <p/>
+ * References to pages are generally related to the OpenPGP Application
+ * on ISO SmartCard Systems specification.
+ */
+ @Override
+ public void connect() throws IOException {
+ mIsoCard = AndroidCard.get(mTag);
+ if (mIsoCard == null) {
+ throw new BaseSecurityTokenNfcActivity.IsoDepNotSupportedException("Tag does not support ISO-DEP (ISO 14443-4)");
+ }
+
+ mIsoCard.setTimeout(TIMEOUT);
+ mIsoCard.connect();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final NfcTransport that = (NfcTransport) o;
+
+ if (mTag != null ? !mTag.equals(that.mTag) : that.mTag != null) return false;
+ if (mIsoCard != null ? !mIsoCard.equals(that.mIsoCard) : that.mIsoCard != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mTag != null ? mTag.hashCode() : 0;
+ result = 31 * result + (mIsoCard != null ? mIsoCard.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
new file mode 100644
index 000000000..0040d6958
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
@@ -0,0 +1,757 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ * Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2013-2014 Signe Rüsch
+ * Copyright (C) 2013-2014 Philipp Jakubeit
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import android.support.annotation.NonNull;
+
+import org.bouncycastle.bcpg.HashAlgorithmTags;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.Iso7816TLV;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Passphrase;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.interfaces.RSAPrivateCrtKey;
+
+import nordpol.Apdu;
+
+/**
+ * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant
+ * devices.
+ * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf
+ */
+public class SecurityTokenHelper {
+ // Fidesmo constants
+ private static final String FIDESMO_APPS_AID_PREFIX = "A000000617";
+
+ private static final byte[] BLANK_FINGERPRINT = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ private Transport mTransport;
+
+ private Passphrase mPin;
+ private Passphrase mAdminPin;
+ private boolean mPw1ValidForMultipleSignatures;
+ private boolean mPw1ValidatedForSignature;
+ private boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming?
+ private boolean mPw3Validated;
+
+ protected SecurityTokenHelper() {
+ }
+
+ public static SecurityTokenHelper getInstance() {
+ return LazyHolder.SECURITY_TOKEN_HELPER;
+ }
+
+ private static String getHex(byte[] raw) {
+ return new String(Hex.encode(raw));
+ }
+
+ private String getHolderName(String name) {
+ try {
+ String slength;
+ int ilength;
+ name = name.substring(6);
+ slength = name.substring(0, 2);
+ ilength = Integer.parseInt(slength, 16) * 2;
+ name = name.substring(2, ilength + 2);
+ name = (new String(Hex.decode(name))).replace('<', ' ');
+ return name;
+ } catch (IndexOutOfBoundsException e) {
+ // try-catch for https://github.com/FluffyKaon/OpenPGP-Card
+ // Note: This should not happen, but happens with
+ // https://github.com/FluffyKaon/OpenPGP-Card, thus return an empty string for now!
+
+ Log.e(Constants.TAG, "Couldn't get holder name, returning empty string!", e);
+ return "";
+ }
+ }
+
+ public Passphrase getPin() {
+ return mPin;
+ }
+
+ public void setPin(final Passphrase pin) {
+ this.mPin = pin;
+ }
+
+ public Passphrase getAdminPin() {
+ return mAdminPin;
+ }
+
+ public void setAdminPin(final Passphrase adminPin) {
+ this.mAdminPin = adminPin;
+ }
+
+ public void changeKey(CanonicalizedSecretKey secretKey, Passphrase passphrase) throws IOException {
+ long keyGenerationTimestamp = secretKey.getCreationTime().getTime() / 1000;
+ byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) keyGenerationTimestamp).array();
+ KeyType keyType = KeyType.from(secretKey);
+
+ if (keyType == null) {
+ throw new IOException("Inappropriate key flags for smart card key.");
+ }
+
+ // Slot is empty, or contains this key already. PUT KEY operation is safe
+ boolean canPutKey = isSlotEmpty(keyType)
+ || keyMatchesFingerPrint(keyType, secretKey.getFingerprint());
+
+ if (!canPutKey) {
+ throw new IOException(String.format("Key slot occupied; card must be reset to put new %s key.",
+ keyType.toString()));
+ }
+
+ putKey(keyType.getmSlot(), secretKey, passphrase);
+ putData(keyType.getmFingerprintObjectId(), secretKey.getFingerprint());
+ putData(keyType.getTimestampObjectId(), timestampBytes);
+ }
+
+ private boolean isSlotEmpty(KeyType keyType) throws IOException {
+ // Note: special case: This should not happen, but happens with
+ // https://github.com/FluffyKaon/OpenPGP-Card, thus for now assume true
+ if (getKeyFingerprint(keyType) == null) return true;
+
+ return keyMatchesFingerPrint(keyType, BLANK_FINGERPRINT);
+ }
+
+ public boolean keyMatchesFingerPrint(KeyType keyType, byte[] fingerprint) throws IOException {
+ return java.util.Arrays.equals(getKeyFingerprint(keyType), fingerprint);
+ }
+
+ /**
+ * Connect to device and select pgp applet
+ *
+ * @throws IOException
+ */
+ public void connectToDevice() throws IOException {
+ // Connect on transport layer
+ mTransport.connect();
+
+ // Connect on smartcard layer
+
+ // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time.
+ // See specification, page 51
+ String accepted = "9000";
+
+ // Command APDU (page 51) for SELECT FILE command (page 29)
+ String opening =
+ "00" // CLA
+ + "A4" // INS
+ + "04" // P1
+ + "00" // P2
+ + "06" // Lc (number of bytes)
+ + "D27600012401" // Data (6 bytes)
+ + "00"; // Le
+ String response = communicate(opening); // activate connection
+ if (!response.endsWith(accepted)) {
+ throw new CardException("Initialization failed!", parseCardStatus(response));
+ }
+
+ byte[] pwStatusBytes = getPwStatusBytes();
+ mPw1ValidForMultipleSignatures = (pwStatusBytes[0] == 1);
+ mPw1ValidatedForSignature = false;
+ mPw1ValidatedForDecrypt = false;
+ mPw3Validated = false;
+ }
+
+ /**
+ * Parses out the status word from a JavaCard response string.
+ *
+ * @param response A hex string with the response from the card
+ * @return A short indicating the SW1/SW2, or 0 if a status could not be determined.
+ */
+ private short parseCardStatus(String response) {
+ if (response.length() < 4) {
+ return 0; // invalid input
+ }
+
+ try {
+ return Short.parseShort(response.substring(response.length() - 4), 16);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Modifies the user's PW1 or PW3. Before sending, the new PIN will be validated for
+ * conformance to the token's requirements for key length.
+ *
+ * @param pw For PW1, this is 0x81. For PW3 (Admin PIN), mode is 0x83.
+ * @param newPin The new PW1 or PW3.
+ */
+ public void modifyPin(int pw, byte[] newPin) throws IOException {
+ final int MAX_PW1_LENGTH_INDEX = 1;
+ final int MAX_PW3_LENGTH_INDEX = 3;
+
+ byte[] pwStatusBytes = getPwStatusBytes();
+
+ if (pw == 0x81) {
+ if (newPin.length < 6 || newPin.length > pwStatusBytes[MAX_PW1_LENGTH_INDEX]) {
+ throw new IOException("Invalid PIN length");
+ }
+ } else if (pw == 0x83) {
+ if (newPin.length < 8 || newPin.length > pwStatusBytes[MAX_PW3_LENGTH_INDEX]) {
+ throw new IOException("Invalid PIN length");
+ }
+ } else {
+ throw new IOException("Invalid PW index for modify PIN operation");
+ }
+
+ byte[] pin;
+ if (pw == 0x83) {
+ pin = mAdminPin.toStringUnsafe().getBytes();
+ } else {
+ pin = mPin.toStringUnsafe().getBytes();
+ }
+
+ // Command APDU for CHANGE REFERENCE DATA command (page 32)
+ String changeReferenceDataApdu = "00" // CLA
+ + "24" // INS
+ + "00" // P1
+ + String.format("%02x", pw) // P2
+ + String.format("%02x", pin.length + newPin.length) // Lc
+ + getHex(pin)
+ + getHex(newPin);
+ String response = communicate(changeReferenceDataApdu); // change PIN
+ if (!response.equals("9000")) {
+ throw new CardException("Failed to change PIN", parseCardStatus(response));
+ }
+ }
+
+ /**
+ * Call DECIPHER command
+ *
+ * @param encryptedSessionKey the encoded session key
+ * @return the decoded session key
+ */
+ public byte[] decryptSessionKey(byte[] encryptedSessionKey) throws IOException {
+ if (!mPw1ValidatedForDecrypt) {
+ verifyPin(0x82); // (Verify PW1 with mode 82 for decryption)
+ }
+
+ String firstApdu = "102a8086fe";
+ String secondApdu = "002a808603";
+ String le = "00";
+
+ byte[] one = new byte[254];
+ // leave out first byte:
+ System.arraycopy(encryptedSessionKey, 1, one, 0, one.length);
+
+ byte[] two = new byte[encryptedSessionKey.length - 1 - one.length];
+ for (int i = 0; i < two.length; i++) {
+ two[i] = encryptedSessionKey[i + one.length + 1];
+ }
+
+ communicate(firstApdu + getHex(one));
+ String second = communicate(secondApdu + getHex(two) + le);
+
+ String decryptedSessionKey = getDataField(second);
+
+ return Hex.decode(decryptedSessionKey);
+ }
+
+ /**
+ * Verifies the user's PW1 or PW3 with the appropriate mode.
+ *
+ * @param mode For PW1, this is 0x81 for signing, 0x82 for everything else.
+ * For PW3 (Admin PIN), mode is 0x83.
+ */
+ private void verifyPin(int mode) throws IOException {
+ if (mPin != null || mode == 0x83) {
+
+ byte[] pin;
+ if (mode == 0x83) {
+ pin = mAdminPin.toStringUnsafe().getBytes();
+ } else {
+ pin = mPin.toStringUnsafe().getBytes();
+ }
+
+ // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time.
+ // See specification, page 51
+ String accepted = "9000";
+ String response = tryPin(mode, pin); // login
+ if (!response.equals(accepted)) {
+ throw new CardException("Bad PIN!", parseCardStatus(response));
+ }
+
+ if (mode == 0x81) {
+ mPw1ValidatedForSignature = true;
+ } else if (mode == 0x82) {
+ mPw1ValidatedForDecrypt = true;
+ } else if (mode == 0x83) {
+ mPw3Validated = true;
+ }
+ }
+ }
+
+ /**
+ * Stores a data object on the token. Automatically validates the proper PIN for the operation.
+ * Supported for all data objects < 255 bytes in length. Only the cardholder certificate
+ * (0x7F21) can exceed this length.
+ *
+ * @param dataObject The data object to be stored.
+ * @param data The data to store in the object
+ */
+ private void putData(int dataObject, byte[] data) throws IOException {
+ if (data.length > 254) {
+ throw new IOException("Cannot PUT DATA with length > 254");
+ }
+ if (dataObject == 0x0101 || dataObject == 0x0103) {
+ if (!mPw1ValidatedForDecrypt) {
+ verifyPin(0x82); // (Verify PW1 for non-signing operations)
+ }
+ } else if (!mPw3Validated) {
+ verifyPin(0x83); // (Verify PW3)
+ }
+
+ String putDataApdu = "00" // CLA
+ + "DA" // INS
+ + String.format("%02x", (dataObject & 0xFF00) >> 8) // P1
+ + String.format("%02x", dataObject & 0xFF) // P2
+ + String.format("%02x", data.length) // Lc
+ + getHex(data);
+
+ String response = communicate(putDataApdu); // put data
+ if (!response.equals("9000")) {
+ throw new CardException("Failed to put data.", parseCardStatus(response));
+ }
+ }
+
+
+ /**
+ * Puts a key on the token in the given slot.
+ *
+ * @param slot The slot on the token where the key should be stored:
+ * 0xB6: Signature Key
+ * 0xB8: Decipherment Key
+ * 0xA4: Authentication Key
+ */
+ private void putKey(int slot, CanonicalizedSecretKey secretKey, Passphrase passphrase)
+ throws IOException {
+ if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) {
+ throw new IOException("Invalid key slot");
+ }
+
+ RSAPrivateCrtKey crtSecretKey;
+ try {
+ secretKey.unlock(passphrase);
+ crtSecretKey = secretKey.getCrtSecretKey();
+ } catch (PgpGeneralException e) {
+ throw new IOException(e.getMessage());
+ }
+
+ // Shouldn't happen; the UI should block the user from getting an incompatible key this far.
+ if (crtSecretKey.getModulus().bitLength() > 2048) {
+ throw new IOException("Key too large to export to Security Token.");
+ }
+
+ // Should happen only rarely; all GnuPG keys since 2006 use public exponent 65537.
+ if (!crtSecretKey.getPublicExponent().equals(new BigInteger("65537"))) {
+ throw new IOException("Invalid public exponent for smart Security Token.");
+ }
+
+ if (!mPw3Validated) {
+ verifyPin(0x83); // (Verify PW3 with mode 83)
+ }
+
+ byte[] header = Hex.decode(
+ "4D82" + "03A2" // Extended header list 4D82, length of 930 bytes. (page 23)
+ + String.format("%02x", slot) + "00" // CRT to indicate targeted key, no length
+ + "7F48" + "15" // Private key template 0x7F48, length 21 (decimal, 0x15 hex)
+ + "9103" // Public modulus, length 3
+ + "928180" // Prime P, length 128
+ + "938180" // Prime Q, length 128
+ + "948180" // Coefficient (1/q mod p), length 128
+ + "958180" // Prime exponent P (d mod (p - 1)), length 128
+ + "968180" // Prime exponent Q (d mod (1 - 1)), length 128
+ + "97820100" // Modulus, length 256, last item in private key template
+ + "5F48" + "820383");// DO 5F48; 899 bytes of concatenated key data will follow
+ byte[] dataToSend = new byte[934];
+ byte[] currentKeyObject;
+ int offset = 0;
+
+ System.arraycopy(header, 0, dataToSend, offset, header.length);
+ offset += header.length;
+ currentKeyObject = crtSecretKey.getPublicExponent().toByteArray();
+ System.arraycopy(currentKeyObject, 0, dataToSend, offset, 3);
+ offset += 3;
+ // NOTE: For a 2048-bit key, these lengths are fixed. However, bigint includes a leading 0
+ // in the array to represent sign, so we take care to set the offset to 1 if necessary.
+ currentKeyObject = crtSecretKey.getPrimeP().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte) 0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getPrimeQ().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte) 0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getCrtCoefficient().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte) 0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getPrimeExponentP().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte) 0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getPrimeExponentQ().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte) 0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getModulus().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 256, dataToSend, offset, 256);
+
+ String putKeyCommand = "10DB3FFF";
+ String lastPutKeyCommand = "00DB3FFF";
+
+ // Now we're ready to communicate with the token.
+ offset = 0;
+ String response;
+ while (offset < dataToSend.length) {
+ int dataRemaining = dataToSend.length - offset;
+ if (dataRemaining > 254) {
+ response = communicate(
+ putKeyCommand + "FE" + Hex.toHexString(dataToSend, offset, 254)
+ );
+ offset += 254;
+ } else {
+ int length = dataToSend.length - offset;
+ response = communicate(
+ lastPutKeyCommand + String.format("%02x", length)
+ + Hex.toHexString(dataToSend, offset, length));
+ offset += length;
+ }
+
+ if (!response.endsWith("9000")) {
+ throw new CardException("Key export to Security Token failed", parseCardStatus(response));
+ }
+ }
+
+ // Clear array with secret data before we return.
+ Arrays.fill(dataToSend, (byte) 0);
+ }
+
+ /**
+ * Return fingerprints of all keys from application specific data stored
+ * on tag, or null if data not available.
+ *
+ * @return The fingerprints of all subkeys in a contiguous byte array.
+ */
+ public byte[] getFingerprints() throws IOException {
+ String data = "00CA006E00";
+ byte[] buf = mTransport.transceive(Hex.decode(data));
+
+ Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true);
+ Log.d(Constants.TAG, "nfcGetFingerprints() Iso7816TLV tlv data:\n" + tlv.prettyPrint());
+
+ Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5);
+ if (fptlv == null) {
+ return null;
+ }
+ return fptlv.mV;
+ }
+
+ /**
+ * Return the PW Status Bytes from the token. This is a simple DO; no TLV decoding needed.
+ *
+ * @return Seven bytes in fixed format, plus 0x9000 status word at the end.
+ */
+ private byte[] getPwStatusBytes() throws IOException {
+ String data = "00CA00C400";
+ return mTransport.transceive(Hex.decode(data));
+ }
+
+ public byte[] getAid() throws IOException {
+ String info = "00CA004F00";
+ return mTransport.transceive(Hex.decode(info));
+ }
+
+ public String getUserId() throws IOException {
+ String info = "00CA006500";
+ return getHolderName(communicate(info));
+ }
+
+ /**
+ * Call COMPUTE DIGITAL SIGNATURE command and returns the MPI value
+ *
+ * @param hash the hash for signing
+ * @return a big integer representing the MPI for the given hash
+ */
+ public byte[] calculateSignature(byte[] hash, int hashAlgo) throws IOException {
+ if (!mPw1ValidatedForSignature) {
+ verifyPin(0x81); // (Verify PW1 with mode 81 for signing)
+ }
+
+ // dsi, including Lc
+ String dsi;
+
+ Log.i(Constants.TAG, "Hash: " + hashAlgo);
+ switch (hashAlgo) {
+ case HashAlgorithmTags.SHA1:
+ if (hash.length != 20) {
+ throw new IOException("Bad hash length (" + hash.length + ", expected 10!");
+ }
+ dsi = "23" // Lc
+ + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes
+ + "3009" // Tag/Length of Sequence, the 0x09 are the following header bytes
+ + "0605" + "2B0E03021A" // OID of SHA1
+ + "0500" // TLV coding of ZERO
+ + "0414" + getHex(hash); // 0x14 are 20 hash bytes
+ break;
+ case HashAlgorithmTags.RIPEMD160:
+ if (hash.length != 20) {
+ throw new IOException("Bad hash length (" + hash.length + ", expected 20!");
+ }
+ dsi = "233021300906052B2403020105000414" + getHex(hash);
+ break;
+ case HashAlgorithmTags.SHA224:
+ if (hash.length != 28) {
+ throw new IOException("Bad hash length (" + hash.length + ", expected 28!");
+ }
+ dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash);
+ break;
+ case HashAlgorithmTags.SHA256:
+ if (hash.length != 32) {
+ throw new IOException("Bad hash length (" + hash.length + ", expected 32!");
+ }
+ dsi = "333031300D060960864801650304020105000420" + getHex(hash);
+ break;
+ case HashAlgorithmTags.SHA384:
+ if (hash.length != 48) {
+ throw new IOException("Bad hash length (" + hash.length + ", expected 48!");
+ }
+ dsi = "433041300D060960864801650304020205000430" + getHex(hash);
+ break;
+ case HashAlgorithmTags.SHA512:
+ if (hash.length != 64) {
+ throw new IOException("Bad hash length (" + hash.length + ", expected 64!");
+ }
+ dsi = "533051300D060960864801650304020305000440" + getHex(hash);
+ break;
+ default:
+ throw new IOException("Not supported hash algo!");
+ }
+
+ // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37)
+ String apdu =
+ "002A9E9A" // CLA, INS, P1, P2
+ + dsi // digital signature input
+ + "00"; // Le
+
+ String response = communicate(apdu);
+
+ if (response.length() < 4) {
+ throw new CardException("Bad response", (short) 0);
+ }
+ // split up response into signature and status
+ String status = response.substring(response.length() - 4);
+ String signature = response.substring(0, response.length() - 4);
+
+ // while we are getting 0x61 status codes, retrieve more data
+ while (status.substring(0, 2).equals("61")) {
+ Log.d(Constants.TAG, "requesting more data, status " + status);
+ // Send GET RESPONSE command
+ response = communicate("00C00000" + status.substring(2));
+ status = response.substring(response.length() - 4);
+ signature += response.substring(0, response.length() - 4);
+ }
+
+ Log.d(Constants.TAG, "final response:" + status);
+
+ if (!mPw1ValidForMultipleSignatures) {
+ mPw1ValidatedForSignature = false;
+ }
+
+ if (!"9000".equals(status)) {
+ throw new CardException("Bad NFC response code: " + status, parseCardStatus(response));
+ }
+
+ // Make sure the signature we received is actually the expected number of bytes long!
+ if (signature.length() != 256 && signature.length() != 512) {
+ throw new IOException("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2);
+ }
+
+ return Hex.decode(signature);
+ }
+
+ /**
+ * Transceive data via NFC encoded as Hex
+ */
+ private String communicate(String apdu) throws IOException {
+ return getHex(mTransport.transceive(Hex.decode(apdu)));
+ }
+
+ public Transport getTransport() {
+ return mTransport;
+ }
+
+ public void setTransport(Transport mTransport) {
+ this.mTransport = mTransport;
+ }
+
+ public boolean isFidesmoToken() {
+ if (isConnected()) { // Check if we can still talk to the card
+ try {
+ // By trying to select any apps that have the Fidesmo AID prefix we can
+ // see if it is a Fidesmo device or not
+ byte[] mSelectResponse = mTransport.transceive(Apdu.select(FIDESMO_APPS_AID_PREFIX));
+ // Compare the status returned by our select with the OK status code
+ return Apdu.hasStatus(mSelectResponse, Apdu.OK_APDU);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Card communication failed!", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Generates a key on the card in the given slot. If the slot is 0xB6 (the signature key),
+ * this command also has the effect of resetting the digital signature counter.
+ * NOTE: This does not set the key fingerprint data object! After calling this command, you
+ * must construct a public key packet using the returned public key data objects, compute the
+ * key fingerprint, and store it on the card using: putData(0xC8, key.getFingerprint())
+ *
+ * @param slot The slot on the card where the key should be generated:
+ * 0xB6: Signature Key
+ * 0xB8: Decipherment Key
+ * 0xA4: Authentication Key
+ * @return the public key data objects, in TLV format. For RSA this will be the public modulus
+ * (0x81) and exponent (0x82). These may come out of order; proper TLV parsing is required.
+ */
+ public byte[] generateKey(int slot) throws IOException {
+ if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) {
+ throw new IOException("Invalid key slot");
+ }
+
+ if (!mPw3Validated) {
+ verifyPin(0x83); // (Verify PW3 with mode 83)
+ }
+
+ String generateKeyApdu = "0047800002" + String.format("%02x", slot) + "0000";
+ String getResponseApdu = "00C00000";
+
+ String first = communicate(generateKeyApdu);
+ String second = communicate(getResponseApdu);
+
+ if (!second.endsWith("9000")) {
+ throw new IOException("On-card key generation failed");
+ }
+
+ String publicKeyData = getDataField(first) + getDataField(second);
+
+ Log.d(Constants.TAG, "Public Key Data Objects: " + publicKeyData);
+
+ return Hex.decode(publicKeyData);
+ }
+
+ private String getDataField(String output) {
+ return output.substring(0, output.length() - 4);
+ }
+
+ private String tryPin(int mode, byte[] pin) throws IOException {
+ // Command APDU for VERIFY command (page 32)
+ String login =
+ "00" // CLA
+ + "20" // INS
+ + "00" // P1
+ + String.format("%02x", mode) // P2
+ + String.format("%02x", pin.length) // Lc
+ + Hex.toHexString(pin);
+
+ return communicate(login);
+ }
+
+ /**
+ * Resets security token, which deletes all keys and data objects.
+ * This works by entering a wrong PIN and then Admin PIN 4 times respectively.
+ * Afterwards, the token is reactivated.
+ */
+ public void resetAndWipeToken() throws IOException {
+ String accepted = "9000";
+
+ // try wrong PIN 4 times until counter goes to C0
+ byte[] pin = "XXXXXX".getBytes();
+ for (int i = 0; i <= 4; i++) {
+ String response = tryPin(0x81, pin);
+ if (response.equals(accepted)) { // Should NOT accept!
+ throw new CardException("Should never happen, XXXXXX has been accepted!", parseCardStatus(response));
+ }
+ }
+
+ // try wrong Admin PIN 4 times until counter goes to C0
+ byte[] adminPin = "XXXXXXXX".getBytes();
+ for (int i = 0; i <= 4; i++) {
+ String response = tryPin(0x83, adminPin);
+ if (response.equals(accepted)) { // Should NOT accept!
+ throw new CardException("Should never happen, XXXXXXXX has been accepted", parseCardStatus(response));
+ }
+ }
+
+ // reactivate token!
+ String reactivate1 = "00" + "e6" + "00" + "00";
+ String reactivate2 = "00" + "44" + "00" + "00";
+ String response1 = communicate(reactivate1);
+ String response2 = communicate(reactivate2);
+ if (!response1.equals(accepted) || !response2.equals(accepted)) {
+ throw new CardException("Reactivating failed!", parseCardStatus(response1));
+ }
+
+ }
+
+ /**
+ * Return the fingerprint from application specific data stored on tag, or
+ * null if it doesn't exist.
+ *
+ * @param keyType key type
+ * @return The fingerprint of the requested key, or null if not found.
+ */
+ public byte[] getKeyFingerprint(@NonNull KeyType keyType) throws IOException {
+ byte[] data = getFingerprints();
+ if (data == null) {
+ return null;
+ }
+
+ // return the master key fingerprint
+ ByteBuffer fpbuf = ByteBuffer.wrap(data);
+ byte[] fp = new byte[20];
+ fpbuf.position(keyType.getIdx() * 20);
+ fpbuf.get(fp, 0, 20);
+
+ return fp;
+ }
+
+ public boolean isPersistentConnectionAllowed() {
+ return mTransport != null && mTransport.isPersistentConnectionAllowed();
+ }
+
+ public boolean isConnected() {
+ return mTransport != null && mTransport.isConnected();
+ }
+
+ private static class LazyHolder {
+ private static final SecurityTokenHelper SECURITY_TOKEN_HELPER = new SecurityTokenHelper();
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java
new file mode 100644
index 000000000..294eaa9ee
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import java.io.IOException;
+
+/**
+ * Abstraction for transmitting APDU commands
+ */
+public interface Transport {
+ /**
+ * Transmit and receive data
+ * @param data data to transmit
+ * @return received data
+ * @throws IOException
+ */
+ byte[] transceive(byte[] data) throws IOException;
+
+ /**
+ * Disconnect and release connection
+ */
+ void release();
+
+ /**
+ * Check if device is was connected to and still is connected
+ * @return connection status
+ */
+ boolean isConnected();
+
+ /**
+ * Check if Transport supports persistent connections e.g connections which can
+ * handle multiple operations in one session
+ * @return true if transport supports persistent connections
+ */
+ boolean isPersistentConnectionAllowed();
+
+
+ /**
+ * Connect to device
+ * @throws IOException
+ */
+ void connect() throws IOException;
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbTransport.java
new file mode 100644
index 000000000..dfe91427e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbTransport.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Pair;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Based on USB CCID Specification rev. 1.1
+ * http://www.usb.org/developers/docs/devclass_docs/DWG_Smart-Card_CCID_Rev110.pdf
+ * Implements small subset of these features
+ */
+public class UsbTransport implements Transport {
+ private static final int USB_CLASS_SMARTCARD = 11;
+ private static final int TIMEOUT = 20 * 1000; // 20s
+
+ private final UsbManager mUsbManager;
+ private final UsbDevice mUsbDevice;
+ private UsbInterface mUsbInterface;
+ private UsbEndpoint mBulkIn;
+ private UsbEndpoint mBulkOut;
+ private UsbDeviceConnection mConnection;
+ private byte mCounter;
+
+ public UsbTransport(UsbDevice usbDevice, UsbManager usbManager) {
+ mUsbDevice = usbDevice;
+ mUsbManager = usbManager;
+ }
+
+
+ /**
+ * Manage ICC power, Yubikey requires to power on ICC
+ * Spec: 6.1.1 PC_to_RDR_IccPowerOn; 6.1.2 PC_to_RDR_IccPowerOff
+ *
+ * @param on true to turn ICC on, false to turn it off
+ * @throws UsbTransportException
+ */
+ private void setIccPower(boolean on) throws UsbTransportException {
+ final byte[] iccPowerCommand = {
+ (byte) (on ? 0x62 : 0x63),
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00,
+ mCounter++,
+ 0x00,
+ 0x00, 0x00
+ };
+
+ sendRaw(iccPowerCommand);
+ byte[] bytes;
+ do {
+ bytes = receive();
+ } while (isDataBlockNotReady(bytes));
+ checkDataBlockResponse(bytes);
+ }
+
+ /**
+ * Get first class 11 (Chip/Smartcard) interface of the device
+ *
+ * @param device {@link UsbDevice} which will be searched
+ * @return {@link UsbInterface} of smartcard or null if it doesn't exist
+ */
+ @Nullable
+ private static UsbInterface getSmartCardInterface(UsbDevice device) {
+ for (int i = 0; i < device.getInterfaceCount(); i++) {
+ UsbInterface anInterface = device.getInterface(i);
+ if (anInterface.getInterfaceClass() == USB_CLASS_SMARTCARD) {
+ return anInterface;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get device's bulk-in and bulk-out endpoints
+ *
+ * @param usbInterface usb device interface
+ * @return pair of builk-in and bulk-out endpoints respectively
+ */
+ @NonNull
+ private static Pair<UsbEndpoint, UsbEndpoint> getIoEndpoints(final UsbInterface usbInterface) {
+ UsbEndpoint bulkIn = null, bulkOut = null;
+ for (int i = 0; i < usbInterface.getEndpointCount(); i++) {
+ final UsbEndpoint endpoint = usbInterface.getEndpoint(i);
+ if (endpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_BULK) {
+ continue;
+ }
+
+ if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) {
+ bulkIn = endpoint;
+ } else if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
+ bulkOut = endpoint;
+ }
+ }
+ return new Pair<>(bulkIn, bulkOut);
+ }
+
+ /**
+ * Release interface and disconnect
+ */
+ @Override
+ public void release() {
+ if (mConnection != null) {
+ mConnection.releaseInterface(mUsbInterface);
+ mConnection.close();
+ mConnection = null;
+ }
+
+ Log.d(Constants.TAG, "Usb transport disconnected");
+ }
+
+ /**
+ * Check if device is was connected to and still is connected
+ * @return true if device is connected
+ */
+ @Override
+ public boolean isConnected() {
+ return mConnection != null && mUsbManager.getDeviceList().containsValue(mUsbDevice) &&
+ mConnection.getSerial() != null;
+ }
+
+ /**
+ * Check if Transport supports persistent connections e.g connections which can
+ * handle multiple operations in one session
+ * @return true if transport supports persistent connections
+ */
+ @Override
+ public boolean isPersistentConnectionAllowed() {
+ return true;
+ }
+
+ /**
+ * Connect to OTG device
+ * @throws IOException
+ */
+ @Override
+ public void connect() throws IOException {
+ mCounter = 0;
+ mUsbInterface = getSmartCardInterface(mUsbDevice);
+ if (mUsbInterface == null) {
+ // Shouldn't happen as we whitelist only class 11 devices
+ throw new UsbTransportException("USB error - device doesn't have class 11 interface");
+ }
+
+ final Pair<UsbEndpoint, UsbEndpoint> ioEndpoints = getIoEndpoints(mUsbInterface);
+ mBulkIn = ioEndpoints.first;
+ mBulkOut = ioEndpoints.second;
+
+ if (mBulkIn == null || mBulkOut == null) {
+ throw new UsbTransportException("USB error - invalid class 11 interface");
+ }
+
+ mConnection = mUsbManager.openDevice(mUsbDevice);
+ if (mConnection == null) {
+ throw new UsbTransportException("USB error - failed to connect to device");
+ }
+
+ if (!mConnection.claimInterface(mUsbInterface, true)) {
+ throw new UsbTransportException("USB error - failed to claim interface");
+ }
+
+ setIccPower(true);
+ Log.d(Constants.TAG, "Usb transport connected");
+ }
+
+ /**
+ * Transmit and receive data
+ * @param data data to transmit
+ * @return received data
+ * @throws UsbTransportException
+ */
+ @Override
+ public byte[] transceive(byte[] data) throws UsbTransportException {
+ sendXfrBlock(data);
+ byte[] bytes;
+ do {
+ bytes = receive();
+ } while (isDataBlockNotReady(bytes));
+
+ checkDataBlockResponse(bytes);
+ // Discard header
+ return Arrays.copyOfRange(bytes, 10, bytes.length);
+ }
+
+ /**
+ * Transmits XfrBlock
+ * 6.1.4 PC_to_RDR_XfrBlock
+ * @param payload payload to transmit
+ * @throws UsbTransportException
+ */
+ private void sendXfrBlock(byte[] payload) throws UsbTransportException {
+ int l = payload.length;
+ byte[] data = Arrays.concatenate(new byte[]{
+ 0x6f,
+ (byte) l, (byte) (l >> 8), (byte) (l >> 16), (byte) (l >> 24),
+ 0x00,
+ mCounter++,
+ 0x00,
+ 0x00, 0x00},
+ payload);
+
+ int send = 0;
+ while (send < data.length) {
+ final int len = Math.min(mBulkIn.getMaxPacketSize(), data.length - send);
+ sendRaw(Arrays.copyOfRange(data, send, send + len));
+ send += len;
+ }
+ }
+
+ private byte[] receive() throws UsbTransportException {
+ byte[] buffer = new byte[mBulkIn.getMaxPacketSize()];
+ byte[] result = null;
+ int readBytes = 0, totalBytes = 0;
+
+ do {
+ int res = mConnection.bulkTransfer(mBulkIn, buffer, buffer.length, TIMEOUT);
+ if (res < 0) {
+ throw new UsbTransportException("USB error - failed to receive response " + res);
+ }
+ if (result == null) {
+ if (res < 10) {
+ throw new UsbTransportException("USB-CCID error - failed to receive CCID header");
+ }
+ totalBytes = ByteBuffer.wrap(buffer, 1, 4).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get() + 10;
+ result = new byte[totalBytes];
+ }
+ System.arraycopy(buffer, 0, result, readBytes, res);
+ readBytes += res;
+ } while (readBytes < totalBytes);
+
+ return result;
+ }
+
+ private void sendRaw(final byte[] data) throws UsbTransportException {
+ final int tr1 = mConnection.bulkTransfer(mBulkOut, data, data.length, TIMEOUT);
+ if (tr1 != data.length) {
+ throw new UsbTransportException("USB error - failed to transmit data " + tr1);
+ }
+ }
+
+ private byte getStatus(byte[] bytes) {
+ return (byte) ((bytes[7] >> 6) & 0x03);
+ }
+
+ private void checkDataBlockResponse(byte[] bytes) throws UsbTransportException {
+ final byte status = getStatus(bytes);
+ if (status != 0) {
+ throw new UsbTransportException("USB-CCID error - status " + status + " error code: " + Hex.toHexString(bytes, 8, 1));
+ }
+ }
+
+ private boolean isDataBlockNotReady(byte[] bytes) {
+ return getStatus(bytes) == 2;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final UsbTransport that = (UsbTransport) o;
+
+ return mUsbDevice != null ? mUsbDevice.equals(that.mUsbDevice) : that.mUsbDevice == null;
+ }
+
+ @Override
+ public int hashCode() {
+ return mUsbDevice != null ? mUsbDevice.hashCode() : 0;
+ }
+
+ public UsbDevice getUsbDevice() {
+ return mUsbDevice;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbTransportException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbTransportException.java
new file mode 100644
index 000000000..6d9212d9f
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbTransportException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import java.io.IOException;
+
+public class UsbTransportException extends IOException {
+ public UsbTransportException() {
+ }
+
+ public UsbTransportException(final String detailMessage) {
+ super(detailMessage);
+ }
+
+ public UsbTransportException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public UsbTransportException(final Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
index 15f8a47db..965d15138 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
@@ -144,12 +144,13 @@ public class ContactSyncAdapterService extends Service {
public static void requestContactsSync() {
// if user has disabled automatic sync, do nothing
- if (!ContentResolver.getSyncAutomatically(
- new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE),
- ContactsContract.AUTHORITY)) {
+ boolean isSyncEnabled = ContentResolver.getSyncAutomatically(new Account
+ (Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), ContactsContract.AUTHORITY);
+
+ if (!isSyncEnabled) {
return;
}
-
+
Bundle extras = new Bundle();
// no need to wait, do it immediately
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
index 1c59782fc..b71fbada8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
@@ -11,11 +11,14 @@ import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.PeriodicSync;
import android.content.SyncResult;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -35,6 +38,7 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.receiver.NetworkReceiver;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -68,7 +72,7 @@ public class KeyserverSyncAdapterService extends Service {
private static final String ACTION_IGNORE_TOR = "ignore_tor";
private static final String ACTION_UPDATE_ALL = "update_all";
- private static final String ACTION_SYNC_NOW = "sync_now";
+ public static final String ACTION_SYNC_NOW = "sync_now";
private static final String ACTION_DISMISS_NOTIFICATION = "cancel_sync";
private static final String ACTION_START_ORBOT = "start_orbot";
private static final String ACTION_CANCEL = "cancel";
@@ -176,8 +180,25 @@ public class KeyserverSyncAdapterService extends Service {
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
- Log.d(Constants.TAG, "Performing a keyserver sync!");
+ Preferences prefs = Preferences.getPreferences(getContext());
+
+ // for a wifi-ONLY sync
+ if (prefs.getWifiOnlySync()) {
+
+ ConnectivityManager connMgr = (ConnectivityManager)
+ getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ boolean isNotOnWifi = !(networkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+ boolean isNotConnected = !(networkInfo.isConnected());
+
+ // if Wi-Fi connection doesn't exist then receiver is enabled
+ if (isNotOnWifi && isNotConnected) {
+ new NetworkReceiver().setWifiReceiverComponent(true, getContext());
+ return;
+ }
+ }
+ Log.d(Constants.TAG, "Performing a keyserver sync!");
PowerManager pm = (PowerManager) KeyserverSyncAdapterService.this
.getSystemService(Context.POWER_SERVICE);
@SuppressWarnings("deprecation") // our min is API 15, deprecated only in 20
@@ -509,6 +530,10 @@ public class KeyserverSyncAdapterService extends Service {
return builder.build();
}
+ /**
+ * creates a new sync if one does not exist, or updates an existing sync if the sync interval
+ * has changed.
+ */
public static void enableKeyserverSync(Context context) {
Account account = KeychainApplication.createAccountIfNecessary(context);
@@ -519,12 +544,26 @@ public class KeyserverSyncAdapterService extends Service {
ContentResolver.setIsSyncable(account, Constants.PROVIDER_AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, Constants.PROVIDER_AUTHORITY, true);
- ContentResolver.addPeriodicSync(
- account,
- Constants.PROVIDER_AUTHORITY,
- new Bundle(),
- SYNC_INTERVAL
- );
+
+ boolean intervalChanged = false;
+ boolean syncExists = Preferences.getKeyserverSyncEnabled(context);
+
+ if (syncExists) {
+ long oldInterval = ContentResolver.getPeriodicSyncs(
+ account, Constants.PROVIDER_AUTHORITY).get(0).period;
+ if (oldInterval != SYNC_INTERVAL) {
+ intervalChanged = true;
+ }
+ }
+
+ if (!syncExists || intervalChanged) {
+ ContentResolver.addPeriodicSync(
+ account,
+ Constants.PROVIDER_AUTHORITY,
+ new Bundle(),
+ SYNC_INTERVAL
+ );
+ }
}
private boolean isSyncEnabled() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 472eb3b18..dc892ecc8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -356,29 +356,21 @@ public class SaveKeyringParcel implements Parcelable {
// The new passphrase to use
public final Passphrase mNewPassphrase;
- // A new pin to use. Must only contain [0-9]+
- public final Passphrase mNewPin;
public ChangeUnlockParcel(Passphrase newPassphrase) {
- this(newPassphrase, null);
- }
- public ChangeUnlockParcel(Passphrase newPassphrase, Passphrase newPin) {
- if (newPassphrase == null && newPin == null) {
- throw new RuntimeException("Cannot set both passphrase and pin. THIS IS A BUG!");
+ if (newPassphrase == null) {
+ throw new AssertionError("newPassphrase must be non-null. THIS IS A BUG!");
}
mNewPassphrase = newPassphrase;
- mNewPin = newPin;
}
public ChangeUnlockParcel(Parcel source) {
mNewPassphrase = source.readParcelable(Passphrase.class.getClassLoader());
- mNewPin = source.readParcelable(Passphrase.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeParcelable(mNewPassphrase, flags);
- destination.writeParcelable(mNewPin, flags);
}
@Override
@@ -397,9 +389,7 @@ public class SaveKeyringParcel implements Parcelable {
};
public String toString() {
- return mNewPassphrase != null
- ? ("passphrase (" + mNewPassphrase + ")")
- : ("pin (" + mNewPin + ")");
+ return "passphrase (" + mNewPassphrase + ")";
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
index 849418905..080c34c04 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
@@ -17,18 +17,18 @@
package org.sufficientlysecure.keychain.service.input;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import org.sufficientlysecure.keychain.util.ParcelableProxy;
-import org.sufficientlysecure.keychain.util.Passphrase;
-import java.net.Proxy;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
+import org.sufficientlysecure.keychain.util.Passphrase;
+
/**
* This is a base class for the input of crypto operations.
*/
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
index 429d7a7e5..84c139d0b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
@@ -14,8 +14,8 @@ import java.util.Date;
public class RequiredInputParcel implements Parcelable {
public enum RequiredInputType {
- PASSPHRASE, PASSPHRASE_SYMMETRIC, BACKUP_CODE, NFC_SIGN, NFC_DECRYPT,
- NFC_MOVE_KEY_TO_CARD, NFC_RESET_CARD, ENABLE_ORBOT, UPLOAD_FAIL_RETRY,
+ PASSPHRASE, PASSPHRASE_SYMMETRIC, BACKUP_CODE, SECURITY_TOKEN_SIGN, SECURITY_TOKEN_DECRYPT,
+ SECURITY_TOKEN_MOVE_KEY_TO_CARD, SECURITY_TOKEN_RESET_CARD, ENABLE_ORBOT, UPLOAD_FAIL_RETRY,
}
public Date mSignatureTime;
@@ -89,22 +89,22 @@ public class RequiredInputParcel implements Parcelable {
return new RequiredInputParcel(RequiredInputType.ENABLE_ORBOT, null, null, null, 0L, 0L);
}
- public static RequiredInputParcel createNfcSignOperation(
+ public static RequiredInputParcel createSecurityTokenSignOperation(
long masterKeyId, long subKeyId,
byte[] inputHash, int signAlgo, Date signatureTime) {
- return new RequiredInputParcel(RequiredInputType.NFC_SIGN,
+ return new RequiredInputParcel(RequiredInputType.SECURITY_TOKEN_SIGN,
new byte[][] { inputHash }, new int[] { signAlgo },
signatureTime, masterKeyId, subKeyId);
}
- public static RequiredInputParcel createNfcDecryptOperation(
+ public static RequiredInputParcel createSecurityTokenDecryptOperation(
long masterKeyId, long subKeyId, byte[] encryptedSessionKey) {
- return new RequiredInputParcel(RequiredInputType.NFC_DECRYPT,
+ return new RequiredInputParcel(RequiredInputType.SECURITY_TOKEN_DECRYPT,
new byte[][] { encryptedSessionKey }, null, null, masterKeyId, subKeyId);
}
- public static RequiredInputParcel createNfcReset() {
- return new RequiredInputParcel(RequiredInputType.NFC_RESET_CARD,
+ public static RequiredInputParcel createSecurityTokenReset() {
+ return new RequiredInputParcel(RequiredInputType.SECURITY_TOKEN_RESET_CARD,
null, null, null, null, null);
}
@@ -188,14 +188,14 @@ public class RequiredInputParcel implements Parcelable {
}
};
- public static class NfcSignOperationsBuilder {
+ public static class SecurityTokenSignOperationsBuilder {
Date mSignatureTime;
ArrayList<Integer> mSignAlgos = new ArrayList<>();
ArrayList<byte[]> mInputHashes = new ArrayList<>();
long mMasterKeyId;
long mSubKeyId;
- public NfcSignOperationsBuilder(Date signatureTime, long masterKeyId, long subKeyId) {
+ public SecurityTokenSignOperationsBuilder(Date signatureTime, long masterKeyId, long subKeyId) {
mSignatureTime = signatureTime;
mMasterKeyId = masterKeyId;
mSubKeyId = subKeyId;
@@ -209,7 +209,7 @@ public class RequiredInputParcel implements Parcelable {
signAlgos[i] = mSignAlgos.get(i);
}
- return new RequiredInputParcel(RequiredInputType.NFC_SIGN,
+ return new RequiredInputParcel(RequiredInputType.SECURITY_TOKEN_SIGN,
inputHashes, signAlgos, mSignatureTime, mMasterKeyId, mSubKeyId);
}
@@ -222,7 +222,7 @@ public class RequiredInputParcel implements Parcelable {
if (!mSignatureTime.equals(input.mSignatureTime)) {
throw new AssertionError("input times must match, this is a programming error!");
}
- if (input.mType != RequiredInputType.NFC_SIGN) {
+ if (input.mType != RequiredInputType.SECURITY_TOKEN_SIGN) {
throw new AssertionError("operation types must match, this is a progrmming error!");
}
@@ -238,13 +238,13 @@ public class RequiredInputParcel implements Parcelable {
}
- public static class NfcKeyToCardOperationsBuilder {
+ public static class SecurityTokenKeyToCardOperationsBuilder {
ArrayList<byte[]> mSubkeysToExport = new ArrayList<>();
Long mMasterKeyId;
byte[] mPin;
byte[] mAdminPin;
- public NfcKeyToCardOperationsBuilder(Long masterKeyId) {
+ public SecurityTokenKeyToCardOperationsBuilder(Long masterKeyId) {
mMasterKeyId = masterKeyId;
}
@@ -264,7 +264,7 @@ public class RequiredInputParcel implements Parcelable {
ByteBuffer buf = ByteBuffer.wrap(mSubkeysToExport.get(0));
// We need to pass in a subkey here...
- return new RequiredInputParcel(RequiredInputType.NFC_MOVE_KEY_TO_CARD,
+ return new RequiredInputParcel(RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD,
inputData, null, null, mMasterKeyId, buf.getLong());
}
@@ -287,7 +287,7 @@ public class RequiredInputParcel implements Parcelable {
if (!mMasterKeyId.equals(input.mMasterKeyId)) {
throw new AssertionError("Master keys must match, this is a programming error!");
}
- if (input.mType != RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
+ if (input.mType != RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD) {
throw new AssertionError("Operation types must match, this is a programming error!");
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java
index 47552bf13..65c51969e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java
@@ -18,14 +18,6 @@
package org.sufficientlysecure.keychain.ui;
-import java.io.File;
-import java.io.IOException;
-import java.security.SecureRandom;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Random;
-
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -41,7 +33,7 @@ import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentManager.OnBackStackChangedListener;
import android.text.Editable;
-import android.text.InputType;
+import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -51,12 +43,9 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
-import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
-import com.github.pinball83.maskededittext.MaskedEditText;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
@@ -70,6 +59,14 @@ import org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.Passphrase;
+import java.io.File;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Random;
+
public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringParcel, ExportResult>
implements OnBackStackChangedListener {
@@ -95,7 +92,7 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
private long[] mMasterKeyIds;
String mBackupCode;
- private MaskedEditText mCodeEditText;
+ private EditText[] mCodeEditText;
private ToolableViewAnimator mStatusAnimator, mTitleAnimator, mCodeFieldsAnimator;
private Integer mBackStackLevel;
@@ -145,8 +142,13 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
boolean newCheckedState = !item.isChecked();
item.setChecked(newCheckedState);
mDebugModeAcceptAnyCode = newCheckedState;
- if (newCheckedState) {
- mCodeEditText.setText("ABCD-EFGH-IJKL-MNOP-QRST-UVWX");
+ if (newCheckedState && TextUtils.isEmpty(mCodeEditText[0].getText())) {
+ mCodeEditText[0].setText("ABCD");
+ mCodeEditText[1].setText("EFGH");
+ mCodeEditText[2].setText("IJKL");
+ mCodeEditText[3].setText("MNOP");
+ mCodeEditText[4].setText("QRST");
+ mCodeEditText[5].setText("UVWX");
Notify.create(getActivity(), "Actual backup code is all 'A's", Style.WARN).show();
}
return true;
@@ -170,11 +172,9 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
mTitleAnimator.setDisplayedChild(1, animate);
mStatusAnimator.setDisplayedChild(1, animate);
mCodeFieldsAnimator.setDisplayedChild(1, animate);
- // use non-breaking spaces to enlarge the empty EditText appropriately
- String empty = "\u00a0\u00a0\u00a0\u00a0-\u00a0\u00a0\u00a0\u00a0" +
- "-\u00a0\u00a0\u00a0\u00a0-\u00a0\u00a0\u00a0\u00a0" +
- "-\u00a0\u00a0\u00a0\u00a0-\u00a0\u00a0\u00a0\u00a0";
- mCodeEditText.setText(empty);
+ for (EditText editText : mCodeEditText) {
+ editText.setText("");
+ }
pushBackStackEntry();
@@ -188,7 +188,7 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
hideKeyboard();
if (animate) {
- @ColorInt int black = mCodeEditText.getCurrentTextColor();
+ @ColorInt int black = mCodeEditText[0].getCurrentTextColor();
@ColorInt int red = getResources().getColor(R.color.android_red_dark);
animateFlashText(mCodeEditText, black, red, false);
}
@@ -203,14 +203,18 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
hideKeyboard();
- mCodeEditText.setEnabled(false);
+ for (EditText editText : mCodeEditText) {
+ editText.setEnabled(false);
+ }
@ColorInt int green = getResources().getColor(R.color.android_green_dark);
if (animate) {
- @ColorInt int black = mCodeEditText.getCurrentTextColor();
+ @ColorInt int black = mCodeEditText[0].getCurrentTextColor();
animateFlashText(mCodeEditText, black, green, true);
} else {
- mCodeEditText.setTextColor(green);
+ for (TextView textView : mCodeEditText) {
+ textView.setTextColor(green);
+ }
}
popBackStackNoAction();
@@ -233,22 +237,38 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
mMasterKeyIds = args.getLongArray(ARG_MASTER_KEY_IDS);
mExportSecret = args.getBoolean(ARG_EXPORT_SECRET);
- // NOTE: order of these method calls matter, see setupAutomaticLinebreak()
- mCodeEditText = (MaskedEditText) view.findViewById(R.id.backup_code_input);
- mCodeEditText.setInputType(
- InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
- setupAutomaticLinebreak(mCodeEditText);
- mCodeEditText.setImeOptions(EditorInfo.IME_ACTION_DONE);
- setupEditTextSuccessListener(mCodeEditText);
-
- TextView codeDisplayText = (TextView) view.findViewById(R.id.backup_code_display);
- setupAutomaticLinebreak(codeDisplayText);
+ mCodeEditText = new EditText[6];
+ mCodeEditText[0] = (EditText) view.findViewById(R.id.backup_code_1);
+ mCodeEditText[1] = (EditText) view.findViewById(R.id.backup_code_2);
+ mCodeEditText[2] = (EditText) view.findViewById(R.id.backup_code_3);
+ mCodeEditText[3] = (EditText) view.findViewById(R.id.backup_code_4);
+ mCodeEditText[4] = (EditText) view.findViewById(R.id.backup_code_5);
+ mCodeEditText[5] = (EditText) view.findViewById(R.id.backup_code_6);
+
+ {
+ TextView[] codeDisplayText = new TextView[6];
+ codeDisplayText[0] = (TextView) view.findViewById(R.id.backup_code_display_1);
+ codeDisplayText[1] = (TextView) view.findViewById(R.id.backup_code_display_2);
+ codeDisplayText[2] = (TextView) view.findViewById(R.id.backup_code_display_3);
+ codeDisplayText[3] = (TextView) view.findViewById(R.id.backup_code_display_4);
+ codeDisplayText[4] = (TextView) view.findViewById(R.id.backup_code_display_5);
+ codeDisplayText[5] = (TextView) view.findViewById(R.id.backup_code_display_6);
+
+ // set backup code in code TextViews
+ char[] backupCode = mBackupCode.toCharArray();
+ for (int i = 0; i < codeDisplayText.length; i++) {
+ codeDisplayText[i].setText(backupCode, i * 5, 4);
+ }
- // set background to null in TextViews - this will retain padding from EditText style!
- // noinspection deprecation, setBackground(Drawable) is API level >=16
- codeDisplayText.setBackgroundDrawable(null);
+ // set background to null in TextViews - this will retain padding from EditText style!
+ for (TextView textView : codeDisplayText) {
+ // noinspection deprecation, setBackground(Drawable) is API level >=16
+ textView.setBackgroundDrawable(null);
+ }
+ }
- codeDisplayText.setText(mBackupCode);
+ setupEditTextFocusNext(mCodeEditText);
+ setupEditTextSuccessListener(mCodeEditText);
mStatusAnimator = (ToolableViewAnimator) view.findViewById(R.id.status_animator);
mTitleAnimator = (ToolableViewAnimator) view.findViewById(R.id.title_animator);
@@ -326,68 +346,76 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
outState.putInt(ARG_BACK_STACK, mBackStackLevel == null ? -1 : mBackStackLevel);
}
- /**
- * Automatic line break with max 6 lines for smaller displays
- * <p/>
- * NOTE: I was not able to get this behaviour using XML!
- * Looks like the order of these method calls matter, see http://stackoverflow.com/a/11171307
- */
- private void setupAutomaticLinebreak(TextView textview) {
- textview.setSingleLine(true);
- textview.setMaxLines(6);
- textview.setHorizontallyScrolling(false);
- }
+ private void setupEditTextSuccessListener(final EditText[] backupCodes) {
+ for (EditText backupCode : backupCodes) {
- private void setupEditTextSuccessListener(final MaskedEditText backupCode) {
- backupCode.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ backupCode.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
+ }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
- @Override
- public void afterTextChanged(Editable s) {
- String currentBackupCode = backupCode.getText().toString();
- boolean inInputState = mCurrentState == BackupCodeState.STATE_INPUT
- || mCurrentState == BackupCodeState.STATE_INPUT_ERROR;
- boolean partIsComplete = (currentBackupCode.indexOf(' ') == -1)
- && (currentBackupCode.indexOf('\u00a0') == -1);
- if (!inInputState || !partIsComplete) {
- return;
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() > 4) {
+ throw new AssertionError("max length of each field is 4!");
+ }
+
+ boolean inInputState = mCurrentState == BackupCodeState.STATE_INPUT
+ || mCurrentState == BackupCodeState.STATE_INPUT_ERROR;
+ boolean partIsComplete = s.length() == 4;
+ if (!inInputState || !partIsComplete) {
+ return;
+ }
+
+ checkIfCodeIsCorrect();
}
+ });
- checkIfCodeIsCorrect(currentBackupCode);
- }
- });
+ }
}
- private void checkIfCodeIsCorrect(String currentBackupCode) {
+ private void checkIfCodeIsCorrect() {
if (Constants.DEBUG && mDebugModeAcceptAnyCode) {
switchState(BackupCodeState.STATE_OK, true);
return;
}
- if (currentBackupCode.equals(mBackupCode)) {
+ StringBuilder backupCodeInput = new StringBuilder(26);
+ for (EditText editText : mCodeEditText) {
+ if (editText.getText().length() < 4) {
+ return;
+ }
+ backupCodeInput.append(editText.getText());
+ backupCodeInput.append('-');
+ }
+ backupCodeInput.deleteCharAt(backupCodeInput.length() - 1);
+
+ // if they don't match, do nothing
+ if (backupCodeInput.toString().equals(mBackupCode)) {
switchState(BackupCodeState.STATE_OK, true);
return;
}
switchState(BackupCodeState.STATE_INPUT_ERROR, true);
+
}
private static void animateFlashText(
- final TextView textView, int color1, int color2, boolean staySecondColor) {
+ final TextView[] textViews, int color1, int color2, boolean staySecondColor) {
ValueAnimator anim = ValueAnimator.ofObject(new ArgbEvaluator(), color1, color2);
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
- textView.setTextColor((Integer) animator.getAnimatedValue());
+ for (TextView textView : textViews) {
+ textView.setTextColor((Integer) animator.getAnimatedValue());
+ }
}
});
anim.setRepeatMode(ValueAnimator.REVERSE);
@@ -398,6 +426,34 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
}
+ private static void setupEditTextFocusNext(final EditText[] backupCodes) {
+ for (int i = 0; i < backupCodes.length - 1; i++) {
+
+ final int next = i + 1;
+
+ backupCodes[i].addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ boolean inserting = before < count;
+ boolean cursorAtEnd = (start + count) == 4;
+
+ if (inserting && cursorAtEnd) {
+ backupCodes[next].requestFocus();
+ }
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ });
+
+ }
+ }
+
private void pushBackStackEntry() {
if (mBackStackLevel != null) {
return;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index 3845e07cb..09149716c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -28,7 +28,8 @@ import org.sufficientlysecure.keychain.ui.base.BaseActivity;
public class CertifyKeyActivity extends BaseActivity {
public static final String EXTRA_RESULT = "operation_result";
- public static final String EXTRA_KEY_IDS = "extra_key_ids";
+ // For sending masterKeyIds to MultiUserIdsFragment to display list of keys
+ public static final String EXTRA_KEY_IDS = MultiUserIdsFragment.EXTRA_KEY_IDS ;
public static final String EXTRA_CERTIFY_KEY_ID = "certify_key_id";
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
index 357b445f0..ad39ff43d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
@@ -62,58 +62,26 @@ import java.util.ArrayList;
import java.util.Date;
public class CertifyKeyFragment
- extends CachingCryptoOperationFragment<CertifyActionsParcel, CertifyResult>
- implements LoaderManager.LoaderCallbacks<Cursor> {
-
- public static final String ARG_CHECK_STATES = "check_states";
+ extends CachingCryptoOperationFragment<CertifyActionsParcel, CertifyResult> {
private CheckBox mUploadKeyCheckbox;
- ListView mUserIds;
private CertifyKeySpinner mCertifyKeySpinner;
- private long[] mPubMasterKeyIds;
-
- public static final String[] USER_IDS_PROJECTION = new String[]{
- UserPackets._ID,
- UserPackets.MASTER_KEY_ID,
- UserPackets.USER_ID,
- UserPackets.IS_PRIMARY,
- UserPackets.IS_REVOKED
- };
- private static final int INDEX_MASTER_KEY_ID = 1;
- private static final int INDEX_USER_ID = 2;
- @SuppressWarnings("unused")
- private static final int INDEX_IS_PRIMARY = 3;
- @SuppressWarnings("unused")
- private static final int INDEX_IS_REVOKED = 4;
-
- private MultiUserIdsAdapter mUserIdsAdapter;
+ private MultiUserIdsFragment mMultiUserIdsFragment;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mPubMasterKeyIds = getActivity().getIntent().getLongArrayExtra(CertifyKeyActivity.EXTRA_KEY_IDS);
- if (mPubMasterKeyIds == null) {
- Log.e(Constants.TAG, "List of key ids to certify missing!");
- getActivity().finish();
- return;
- }
-
- ArrayList<Boolean> checkedStates;
- if (savedInstanceState != null) {
- checkedStates = (ArrayList<Boolean>) savedInstanceState.getSerializable(ARG_CHECK_STATES);
- // key spinner and the checkbox keep their own state
- } else {
- checkedStates = null;
-
+ if (savedInstanceState == null) {
// preselect certify key id if given
long certifyKeyId = getActivity().getIntent()
.getLongExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none);
if (certifyKeyId != Constants.key.none) {
try {
- CachedPublicKeyRing key = (new ProviderHelper(getActivity())).getCachedPublicKeyRing(certifyKeyId);
+ CachedPublicKeyRing key = (new ProviderHelper(getActivity()))
+ .getCachedPublicKeyRing(certifyKeyId);
if (key.canCertify()) {
mCertifyKeySpinner.setPreSelectedKeyId(certifyKeyId);
}
@@ -121,15 +89,8 @@ public class CertifyKeyFragment
Log.e(Constants.TAG, "certify certify check failed", e);
}
}
-
}
- mUserIdsAdapter = new MultiUserIdsAdapter(getActivity(), null, 0, checkedStates);
- mUserIds.setAdapter(mUserIdsAdapter);
- mUserIds.setDividerHeight(0);
-
- getLoaderManager().initLoader(0, null, this);
-
OperationResult result = getActivity().getIntent().getParcelableExtra(CertifyKeyActivity.EXTRA_RESULT);
if (result != null) {
// display result from import
@@ -138,21 +99,13 @@ public class CertifyKeyFragment
}
@Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- ArrayList<Boolean> states = mUserIdsAdapter.getCheckStates();
- // no proper parceling method available :(
- outState.putSerializable(ARG_CHECK_STATES, states);
- }
-
- @Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.certify_key_fragment, null);
mCertifyKeySpinner = (CertifyKeySpinner) view.findViewById(R.id.certify_key_spinner);
mUploadKeyCheckbox = (CheckBox) view.findViewById(R.id.sign_key_upload_checkbox);
- mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
+ mMultiUserIdsFragment = (MultiUserIdsFragment)
+ getChildFragmentManager().findFragmentById(R.id.multi_user_ids_fragment);
// make certify image gray, like action icons
ImageView vActionCertifyImage =
@@ -184,127 +137,10 @@ public class CertifyKeyFragment
}
@Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- Uri uri = UserPackets.buildUserIdsUri();
-
- String selection, ids[];
- {
- // generate placeholders and string selection args
- ids = new String[mPubMasterKeyIds.length];
- StringBuilder placeholders = new StringBuilder("?");
- for (int i = 0; i < mPubMasterKeyIds.length; i++) {
- ids[i] = Long.toString(mPubMasterKeyIds[i]);
- if (i != 0) {
- placeholders.append(",?");
- }
- }
- // put together selection string
- selection = UserPackets.IS_REVOKED + " = 0" + " AND "
- + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
- + " IN (" + placeholders + ")";
- }
-
- return new CursorLoader(getActivity(), uri,
- USER_IDS_PROJECTION, selection, ids,
- Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " ASC"
- + ", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC"
- );
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-
- MatrixCursor matrix = new MatrixCursor(new String[]{
- "_id", "user_data", "grouped"
- }) {
- @Override
- public byte[] getBlob(int column) {
- return super.getBlob(column);
- }
- };
- data.moveToFirst();
-
- long lastMasterKeyId = 0;
- String lastName = "";
- ArrayList<String> uids = new ArrayList<>();
-
- boolean header = true;
-
- // Iterate over all rows
- while (!data.isAfterLast()) {
- long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
- String userId = data.getString(INDEX_USER_ID);
- KeyRing.UserId pieces = KeyRing.splitUserId(userId);
-
- // Two cases:
-
- boolean grouped = masterKeyId == lastMasterKeyId;
- boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces.name);
- // Remember for next loop
- lastName = pieces.name;
-
- Log.d(Constants.TAG, Long.toString(masterKeyId, 16) + (grouped ? "grouped" : "not grouped"));
-
- if (!subGrouped) {
- // 1. This name should NOT be grouped with the previous, so we flush the buffer
-
- Parcel p = Parcel.obtain();
- p.writeStringList(uids);
- byte[] d = p.marshall();
- p.recycle();
-
- matrix.addRow(new Object[]{
- lastMasterKeyId, d, header ? 1 : 0
- });
- // indicate that we have a header for this masterKeyId
- header = false;
-
- // Now clear the buffer, and add the new user id, for the next round
- uids.clear();
-
- }
-
- // 2. This name should be grouped with the previous, just add to buffer
- uids.add(userId);
- lastMasterKeyId = masterKeyId;
-
- // If this one wasn't grouped, the next one's gotta be a header
- if (!grouped) {
- header = true;
- }
-
- // Regardless of the outcome, move to next entry
- data.moveToNext();
-
- }
-
- // If there is anything left in the buffer, flush it one last time
- if (!uids.isEmpty()) {
-
- Parcel p = Parcel.obtain();
- p.writeStringList(uids);
- byte[] d = p.marshall();
- p.recycle();
-
- matrix.addRow(new Object[]{
- lastMasterKeyId, d, header ? 1 : 0
- });
-
- }
-
- mUserIdsAdapter.swapCursor(matrix);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- mUserIdsAdapter.swapCursor(null);
- }
-
- @Override
public CertifyActionsParcel createOperationInput() {
// Bail out if there is not at least one user id selected
- ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
+ ArrayList<CertifyAction> certifyActions = mMultiUserIdsFragment.getSelectedCertifyActions();
if (certifyActions.isEmpty()) {
Notify.create(getActivity(), "No identities selected!",
Notify.Style.ERROR).show();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
index b1fec3aae..44b185c52 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -47,9 +47,9 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
public static final String EXTRA_SECURITY_TOKEN_PIN = "yubi_key_pin";
public static final String EXTRA_SECURITY_TOKEN_ADMIN_PIN = "yubi_key_admin_pin";
- public static final String EXTRA_NFC_USER_ID = "nfc_user_id";
- public static final String EXTRA_NFC_AID = "nfc_aid";
- public static final String EXTRA_NFC_FINGERPRINTS = "nfc_fingerprints";
+ public static final String EXTRA_SECURITY_TOKEN_USER_ID = "nfc_user_id";
+ public static final String EXTRA_SECURITY_TOKEN_AID = "nfc_aid";
+ public static final String EXTRA_SECURITY_FINGERPRINTS = "nfc_fingerprints";
public static final String FRAGMENT_TAG = "currentFragment";
@@ -66,8 +66,8 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
byte[] mScannedFingerprints;
- byte[] mNfcAid;
- String mNfcUserId;
+ byte[] mSecurityTokenAid;
+ String mSecurityTokenUserId;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -107,10 +107,10 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
mFirstTime = intent.getBooleanExtra(EXTRA_FIRST_TIME, false);
mCreateSecurityToken = intent.getBooleanExtra(EXTRA_CREATE_SECURITY_TOKEN, false);
- if (intent.hasExtra(EXTRA_NFC_FINGERPRINTS)) {
- byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS);
- String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID);
- byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID);
+ if (intent.hasExtra(EXTRA_SECURITY_FINGERPRINTS)) {
+ byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_SECURITY_FINGERPRINTS);
+ String nfcUserId = intent.getStringExtra(EXTRA_SECURITY_TOKEN_USER_ID);
+ byte[] nfcAid = intent.getByteArrayExtra(EXTRA_SECURITY_TOKEN_AID);
if (containsKeys(nfcFingerprints)) {
Fragment frag = CreateSecurityTokenImportResetFragment.newInstance(
@@ -143,24 +143,29 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
}
@Override
- protected void doNfcInBackground() throws IOException {
- if (mCurrentFragment instanceof NfcListenerFragment) {
- ((NfcListenerFragment) mCurrentFragment).doNfcInBackground();
+ protected void doSecurityTokenInBackground() throws IOException {
+ if (mCurrentFragment instanceof SecurityTokenListenerFragment) {
+ ((SecurityTokenListenerFragment) mCurrentFragment).doSecurityTokenInBackground();
return;
}
- mScannedFingerprints = nfcGetFingerprints();
- mNfcAid = nfcGetAid();
- mNfcUserId = nfcGetUserId();
+ mScannedFingerprints = mSecurityTokenHelper.getFingerprints();
+ mSecurityTokenAid = mSecurityTokenHelper.getAid();
+ mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
}
@Override
- protected void onNfcPostExecute() {
- if (mCurrentFragment instanceof NfcListenerFragment) {
- ((NfcListenerFragment) mCurrentFragment).onNfcPostExecute();
+ protected void onSecurityTokenPostExecute() {
+ if (mCurrentFragment instanceof SecurityTokenListenerFragment) {
+ ((SecurityTokenListenerFragment) mCurrentFragment).onSecurityTokenPostExecute();
return;
}
+ // We don't want get back to wait activity mainly because it looks weird with otg token
+ if (mCurrentFragment instanceof CreateSecurityTokenWaitFragment) {
+ getSupportFragmentManager().popBackStackImmediate();
+ }
+
if (containsKeys(mScannedFingerprints)) {
try {
long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mScannedFingerprints);
@@ -169,15 +174,15 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
Intent intent = new Intent(this, ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mNfcUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mScannedFingerprints);
startActivity(intent);
finish();
} catch (PgpKeyNotFoundException e) {
Fragment frag = CreateSecurityTokenImportResetFragment.newInstance(
- mScannedFingerprints, mNfcAid, mNfcUserId);
+ mScannedFingerprints, mSecurityTokenAid, mSecurityTokenUserId);
loadFragment(frag, FragAction.TO_RIGHT);
}
} else {
@@ -255,9 +260,9 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
}
- interface NfcListenerFragment {
- void doNfcInBackground() throws IOException;
- void onNfcPostExecute();
+ interface SecurityTokenListenerFragment {
+ void doSecurityTokenInBackground() throws IOException;
+ void onSecurityTokenPostExecute();
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
index b020a0dba..b871f471c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
@@ -44,7 +44,6 @@ import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
import java.util.ArrayList;
import java.util.List;
-import java.util.regex.Pattern;
public class CreateKeyEmailFragment extends Fragment {
private CreateKeyActivity mCreateKeyActivity;
@@ -52,10 +51,6 @@ public class CreateKeyEmailFragment extends Fragment {
private ArrayList<EmailAdapter.ViewModel> mAdditionalEmailModels = new ArrayList<>();
private EmailAdapter mEmailAdapter;
- // NOTE: Do not use more complicated pattern like defined in android.util.Patterns.EMAIL_ADDRESS
- // EMAIL_ADDRESS fails for mails with umlauts for example
- private static final Pattern EMAIL_PATTERN = Pattern.compile("^[\\S]+@[\\S]+\\.[a-z]+$");
-
/**
* Creates new instance of this fragment
*/
@@ -76,16 +71,15 @@ public class CreateKeyEmailFragment extends Fragment {
* @return true if EditText is not empty
*/
private boolean isMainEmailValid(EditText editText) {
- boolean output = true;
- if (!checkEmail(editText.getText().toString(), false)) {
+ if (editText.getText().length() == 0) {
editText.setError(getString(R.string.create_key_empty));
editText.requestFocus();
- output = false;
- } else {
- editText.setError(null);
+ return false;
+ } else if (!checkEmail(editText.getText().toString(), false)){
+ return false;
}
-
- return output;
+ editText.setError(null);
+ return true;
}
@Override
@@ -146,10 +140,9 @@ public class CreateKeyEmailFragment extends Fragment {
* @return
*/
private boolean checkEmail(String email, boolean additionalEmail) {
- // check for email format or if the user did any input
- if (!isEmailFormatValid(email)) {
+ if (email.isEmpty()) {
Notify.create(getActivity(),
- getString(R.string.create_key_email_invalid_email),
+ getString(R.string.create_key_email_empty_email),
Notify.LENGTH_LONG, Notify.Style.ERROR).show(CreateKeyEmailFragment.this);
return false;
}
@@ -167,18 +160,6 @@ public class CreateKeyEmailFragment extends Fragment {
}
/**
- * Checks the email format
- * Uses the default Android Email Pattern
- *
- * @param email
- * @return
- */
- private boolean isEmailFormatValid(String email) {
- // check for email format or if the user did any input
- return !(email.length() == 0 || !EMAIL_PATTERN.matcher(email).matches());
- }
-
- /**
* Checks for duplicated emails inside the additional email adapter.
*
* @param email
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
index b53bfc1d0..896df0ad2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -57,6 +57,7 @@ import org.sufficientlysecure.keychain.util.Preferences;
import java.util.Date;
import java.util.Iterator;
+import java.util.regex.Pattern;
public class CreateKeyFinalFragment extends Fragment {
@@ -81,6 +82,10 @@ public class CreateKeyFinalFragment extends Fragment {
private OperationResult mQueuedFinishResult;
private EditKeyResult mQueuedDisplayResult;
+ // NOTE: Do not use more complicated pattern like defined in android.util.Patterns.EMAIL_ADDRESS
+ // EMAIL_ADDRESS fails for mails with umlauts for example
+ private static final Pattern EMAIL_PATTERN = Pattern.compile("^[\\S]+@[\\S]+\\.[a-z]+$");
+
public static CreateKeyFinalFragment newInstance() {
CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
frag.setRetainInstance(true);
@@ -106,7 +111,11 @@ public class CreateKeyFinalFragment extends Fragment {
CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
// set values
- mNameEdit.setText(createKeyActivity.mName);
+ if (createKeyActivity.mName != null) {
+ mNameEdit.setText(createKeyActivity.mName);
+ } else {
+ mNameEdit.setText(getString(R.string.user_id_no_name));
+ }
if (createKeyActivity.mAdditionalEmails != null && createKeyActivity.mAdditionalEmails.size() > 0) {
String emailText = createKeyActivity.mEmail + ", ";
Iterator<?> it = createKeyActivity.mAdditionalEmails.iterator();
@@ -122,6 +131,8 @@ public class CreateKeyFinalFragment extends Fragment {
mEmailEdit.setText(createKeyActivity.mEmail);
}
+ checkEmailValidity();
+
mCreateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -278,17 +289,17 @@ public class CreateKeyFinalFragment extends Fragment {
2048, null, KeyFlags.AUTHENTICATION, 0L));
// use empty passphrase
- saveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase(), null);
+ saveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
} else {
saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
- 4096, null, KeyFlags.CERTIFY_OTHER, 0L));
+ 3072, null, KeyFlags.CERTIFY_OTHER, 0L));
saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
- 4096, null, KeyFlags.SIGN_DATA, 0L));
+ 3072, null, KeyFlags.SIGN_DATA, 0L));
saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
- 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
+ 3072, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
saveKeyringParcel.mNewUnlock = createKeyActivity.mPassphrase != null
- ? new ChangeUnlockParcel(createKeyActivity.mPassphrase, null)
+ ? new ChangeUnlockParcel(createKeyActivity.mPassphrase)
: null;
}
String userId = KeyRing.createUserId(
@@ -309,6 +320,31 @@ public class CreateKeyFinalFragment extends Fragment {
return saveKeyringParcel;
}
+ private void checkEmailValidity() {
+ CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
+
+ boolean emailsValid = true;
+ if (!EMAIL_PATTERN.matcher(createKeyActivity.mEmail).matches()) {
+ emailsValid = false;
+ }
+ if (createKeyActivity.mAdditionalEmails != null && createKeyActivity.mAdditionalEmails.size() > 0) {
+ for (Iterator<?> it = createKeyActivity.mAdditionalEmails.iterator(); it.hasNext(); ) {
+ if (!EMAIL_PATTERN.matcher(it.next().toString()).matches()) {
+ emailsValid = false;
+ }
+ }
+ }
+ if (!emailsValid) {
+ mEmailEdit.setError(getString(R.string.create_key_final_email_valid_warning));
+ mEmailEdit.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mNameEdit.requestFocus(); // Workaround to remove focus from email
+ }
+ });
+ }
+ }
+
private void createKey() {
CreateKeyActivity activity = (CreateKeyActivity) getActivity();
if (activity == null) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java
index 7480367bb..3332b9cf9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java
@@ -18,13 +18,11 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.EditText;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
@@ -50,27 +48,6 @@ public class CreateKeyNameFragment extends Fragment {
return frag;
}
- /**
- * Checks if text of given EditText is not empty. If it is empty an error is
- * set and the EditText gets the focus.
- *
- * @param context
- * @param editText
- * @return true if EditText is not empty
- */
- private static boolean isEditTextNotEmpty(Context context, EditText editText) {
- boolean output = true;
- if (editText.getText().length() == 0) {
- editText.setError(context.getString(R.string.create_key_empty));
- editText.requestFocus();
- output = false;
- } else {
- editText.setError(null);
- }
-
- return output;
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_key_name_fragment, container, false);
@@ -109,13 +86,11 @@ public class CreateKeyNameFragment extends Fragment {
}
private void nextClicked() {
- if (isEditTextNotEmpty(getActivity(), mNameEdit)) {
- // save state
- mCreateKeyActivity.mName = mNameEdit.getText().toString();
+ // save state
+ mCreateKeyActivity.mName = mNameEdit.getText().length() == 0 ? null : mNameEdit.getText().toString();
- CreateKeyEmailFragment frag = CreateKeyEmailFragment.newInstance();
- mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
- }
+ CreateKeyEmailFragment frag = CreateKeyEmailFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
index ea57fe558..6f35fdd38 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
@@ -25,7 +25,6 @@ import java.util.ArrayList;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -43,7 +42,7 @@ import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
-import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity.SecurityTokenListenerFragment;
import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -51,7 +50,7 @@ import org.sufficientlysecure.keychain.util.Preferences;
public class CreateSecurityTokenImportResetFragment
extends QueueingCryptoOperationFragment<ImportKeyringParcel, ImportKeyResult>
- implements NfcListenerFragment {
+ implements SecurityTokenListenerFragment {
private static final int REQUEST_CODE_RESET = 0x00005001;
@@ -231,7 +230,7 @@ public class CreateSecurityTokenImportResetFragment
public void resetCard() {
Intent intent = new Intent(getActivity(), SecurityTokenOperationActivity.class);
- RequiredInputParcel resetP = RequiredInputParcel.createNfcReset();
+ RequiredInputParcel resetP = RequiredInputParcel.createSecurityTokenReset();
intent.putExtra(SecurityTokenOperationActivity.EXTRA_REQUIRED_INPUT, resetP);
intent.putExtra(SecurityTokenOperationActivity.EXTRA_CRYPTO_INPUT, new CryptoInputParcel());
startActivityForResult(intent, REQUEST_CODE_RESET);
@@ -248,11 +247,11 @@ public class CreateSecurityTokenImportResetFragment
}
@Override
- public void doNfcInBackground() throws IOException {
+ public void doSecurityTokenInBackground() throws IOException {
- mTokenFingerprints = mCreateKeyActivity.nfcGetFingerprints();
- mTokenAid = mCreateKeyActivity.nfcGetAid();
- mTokenUserId = mCreateKeyActivity.nfcGetUserId();
+ mTokenFingerprints = mCreateKeyActivity.getSecurityTokenHelper().getFingerprints();
+ mTokenAid = mCreateKeyActivity.getSecurityTokenHelper().getAid();
+ mTokenUserId = mCreateKeyActivity.getSecurityTokenHelper().getUserId();
byte[] fp = new byte[20];
ByteBuffer.wrap(fp).put(mTokenFingerprints, 0, 20);
@@ -260,7 +259,7 @@ public class CreateSecurityTokenImportResetFragment
}
@Override
- public void onNfcPostExecute() {
+ public void onSecurityTokenPostExecute() {
setData();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
index a3ea38e40..5dc2c478b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.os.Bundle;
+import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -26,6 +27,7 @@ import android.view.ViewGroup;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenNfcActivity;
public class CreateSecurityTokenWaitFragment extends Fragment {
@@ -34,6 +36,15 @@ public class CreateSecurityTokenWaitFragment extends Fragment {
View mBackButton;
@Override
+ public void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (this.getActivity() instanceof BaseSecurityTokenNfcActivity) {
+ ((BaseSecurityTokenNfcActivity) this.getActivity()).checkDeviceConnection();
+ }
+ }
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_security_token_wait_fragment, container, false);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index 2d94d0d93..9ed8e369d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -35,6 +35,7 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
@@ -339,8 +340,7 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
// cache new returned passphrase!
mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(
- (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
- null
+ (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE)
);
}
}
@@ -441,50 +441,45 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
}
break;
}
- case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_CARD: {
- // TODO: enable later when Admin PIN handling is resolved
- Notify.create(getActivity(),
- "This feature will be available in an upcoming OpenKeychain version.",
- Notify.Style.WARN).show();
- break;
+ case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_SECURITY_TOKEN: {
+ SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
+ if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
+ secretKeyType == SecretKeyType.GNU_DUMMY) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_stripped, Notify.Style.ERROR)
+ .show();
+ break;
+ }
+
+ int algorithm = mSubkeysAdapter.getAlgorithm(position);
+ if (algorithm != PublicKeyAlgorithmTags.RSA_GENERAL
+ && algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT
+ && algorithm != PublicKeyAlgorithmTags.RSA_SIGN) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_algo, Notify.Style.ERROR)
+ .show();
+ break;
+ }
+
+ if (mSubkeysAdapter.getKeySize(position) != 2048) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_size, Notify.Style.ERROR)
+ .show();
+ break;
+ }
-// Activity activity = EditKeyFragment.this.getActivity();
-// SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
-// if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
-// secretKeyType == SecretKeyType.GNU_DUMMY) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_stripped, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-// int algorithm = mSubkeysAdapter.getAlgorithm(position);
-// // these are the PGP constants for RSA_GENERAL, RSA_ENCRYPT and RSA_SIGN
-// if (algorithm != 1 && algorithm != 2 && algorithm != 3) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_algo, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-// if (mSubkeysAdapter.getKeySize(position) != 2048) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_size, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-//
-//
-// SubkeyChange change;
-// change = mSaveKeyringParcel.getSubkeyChange(keyId);
-// if (change == null) {
-// mSaveKeyringParcel.mChangeSubKeys.add(
-// new SubkeyChange(keyId, false, true)
-// );
-// break;
-// }
-// // toggle
-// change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
-// if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
-// // User had chosen to strip key, but now wants to divert it.
-// change.mDummyStrip = false;
-// }
-// break;
+ SubkeyChange change;
+ change = mSaveKeyringParcel.getSubkeyChange(keyId);
+ if (change == null) {
+ mSaveKeyringParcel.mChangeSubKeys.add(
+ new SubkeyChange(keyId, false, true)
+ );
+ break;
+ }
+ // toggle
+ change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
+ if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
+ // User had chosen to strip key, but now wants to divert it.
+ change.mDummyStrip = false;
+ }
+ break;
}
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
@@ -562,15 +557,9 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
}
private void addSubkey() {
- boolean willBeMasterKey;
- if (mSubkeysAdapter != null) {
- willBeMasterKey = mSubkeysAdapter.getCount() == 0 && mSubkeysAddedAdapter.getCount() == 0;
- } else {
- willBeMasterKey = mSubkeysAddedAdapter.getCount() == 0;
- }
-
+ // new subkey will never be a masterkey, as masterkey cannot be removed
AddSubkeyDialogFragment addSubkeyDialogFragment =
- AddSubkeyDialogFragment.newInstance(willBeMasterKey);
+ AddSubkeyDialogFragment.newInstance(false);
addSubkeyDialogFragment
.setOnAlgorithmSelectedListener(
new AddSubkeyDialogFragment.OnAlgorithmSelectedListener() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
index be08f6a53..d5c540856 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
@@ -247,10 +247,10 @@ public class EncryptFilesFragment
try {
mFilesAdapter.add(inputUri);
} catch (IOException e) {
+ String fileName = FileHelper.getFilename(getActivity(), inputUri);
Notify.create(getActivity(),
- getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)),
+ getActivity().getString(R.string.error_file_added_already, fileName),
Notify.Style.ERROR).show(this);
- return;
}
// remove from pending input uris
@@ -729,6 +729,8 @@ public class EncryptFilesFragment
// make sure this is correct at this point
mAfterEncryptAction = AfterEncryptAction.SAVE;
cryptoOperation(new CryptoInputParcel(new Date()));
+ } else if (resultCode == Activity.RESULT_CANCELED) {
+ onCryptoOperationCancelled();
}
return;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
index ca5d20fb9..51022094b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
@@ -21,6 +21,7 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.ViewAnimator;
import com.tokenautocomplete.TokenCompleteTextView;
@@ -79,9 +80,6 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment {
mSignKeySpinner = (KeySpinner) view.findViewById(R.id.sign);
mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list);
mEncryptKeyView.setThreshold(1); // Start working from first character
- // TODO: workaround for bug in TokenAutoComplete,
- // see https://github.com/open-keychain/open-keychain/issues/1636
- mEncryptKeyView.setDeletionStyle(TokenCompleteTextView.TokenDeleteStyle.ToString);
final ViewAnimator vSignatureIcon = (ViewAnimator) view.findViewById(R.id.result_signature_icon);
mSignKeySpinner.setOnKeyChangedListener(new OnKeyChangedListener() {
@@ -112,6 +110,14 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment {
}
});
+ ImageView addRecipientImgView = (ImageView) view.findViewById(R.id.add_recipient);
+ addRecipientImgView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mEncryptKeyView.showAllKeys();
+ }
+ });
+
return view;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 72e42eec3..7d2d30c35 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -416,11 +416,18 @@ public class ImportKeysActivity extends BaseActivity
intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
setResult(RESULT_OK, intent);
finish();
- return;
+ } else if (result.isOkNew() || result.isOkUpdated()) {
+ // User has successfully imported a key, hide first time dialog
+ Preferences.getPreferences(this).setFirstTime(false);
+
+ // Close activities opened for importing keys and go to the list of keys
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ } else {
+ result.createNotify(ImportKeysActivity.this)
+ .show((ViewGroup) findViewById(R.id.import_snackbar));
}
-
- result.createNotify(ImportKeysActivity.this)
- .show((ViewGroup) findViewById(R.id.import_snackbar));
}
// methods from CryptoOperationHelper.Callback
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
index 8de60dfd3..133cf299f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
@@ -17,29 +17,46 @@
package org.sufficientlysecure.keychain.ui;
+import android.Manifest;
import android.app.Activity;
+import android.content.ContentResolver;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.ui.ImportKeysListFragment.BytesLoaderState;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.FileHelper;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
public class ImportKeysFileFragment extends Fragment {
private ImportKeysActivity mImportActivity;
private View mBrowse;
private View mClipboardButton;
- public static final int REQUEST_CODE_FILE = 0x00007003;
+ private Uri mCurrentUri;
+
+ private static final int REQUEST_CODE_FILE = 0x00007003;
+ private static final int REQUEST_PERMISSION_READ_EXTERNAL_STORAGE = 12;
/**
* Creates new instance of this fragment
@@ -83,10 +100,10 @@ public class ImportKeysFileFragment extends Fragment {
sendText = clipboardText.toString();
sendText = PgpHelper.getPgpKeyContent(sendText);
if (sendText == null) {
- Notify.create(mImportActivity, "Bad data!", Style.ERROR).show();
+ Notify.create(mImportActivity, R.string.error_bad_data, Style.ERROR).show();
return;
}
- mImportActivity.loadCallback(new ImportKeysListFragment.BytesLoaderState(sendText.getBytes(), null));
+ mImportActivity.loadCallback(new BytesLoaderState(sendText.getBytes(), null));
}
}
});
@@ -106,11 +123,12 @@ public class ImportKeysFileFragment extends Fragment {
switch (requestCode) {
case REQUEST_CODE_FILE: {
if (resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
+ mCurrentUri = data.getData();
- // load data
- mImportActivity.loadCallback(new ImportKeysListFragment.BytesLoaderState(null, data.getData()));
+ if (checkAndRequestReadPermission(mCurrentUri)) {
+ startImportingKeys();
+ }
}
-
break;
}
@@ -121,4 +139,77 @@ public class ImportKeysFileFragment extends Fragment {
}
}
+ private void startImportingKeys() {
+ boolean isEncrypted;
+ try {
+ isEncrypted = FileHelper.isEncryptedFile(mImportActivity, mCurrentUri);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error opening file", e);
+
+ Notify.create(mImportActivity, R.string.error_bad_data, Style.ERROR).show();
+ return;
+ }
+
+ if (isEncrypted) {
+ Intent intent = new Intent(mImportActivity, DecryptActivity.class);
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setData(mCurrentUri);
+ startActivity(intent);
+ } else {
+ mImportActivity.loadCallback(new BytesLoaderState(null, mCurrentUri));
+ }
+ }
+
+ /**
+ * Request READ_EXTERNAL_STORAGE permission on Android >= 6.0 to read content from "file" Uris.
+ * <p/>
+ * This method returns true on Android < 6, or if permission is already granted. It
+ * requests the permission and returns false otherwise.
+ * <p/>
+ * see https://commonsware.com/blog/2015/10/07/runtime-permissions-files-action-send.html
+ */
+ private boolean checkAndRequestReadPermission(final Uri uri) {
+ if (!ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
+ return true;
+ }
+
+ // Additional check due to https://commonsware.com/blog/2015/11/09/you-cannot-hold-nonexistent-permissions.html
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ return true;
+ }
+
+ if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+
+ requestPermissions(
+ new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ REQUEST_PERMISSION_READ_EXTERNAL_STORAGE);
+
+ return false;
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+
+ if (requestCode != REQUEST_PERMISSION_READ_EXTERNAL_STORAGE) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ return;
+ }
+
+ boolean permissionWasGranted = grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED;
+
+ if (permissionWasGranted) {
+ startImportingKeys();
+ } else {
+ Toast.makeText(getActivity(), R.string.error_denied_storage_permission, Toast.LENGTH_LONG).show();
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ getActivity().finish();
+ }
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index b399af950..4d4219f56 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -330,9 +330,16 @@ public class ImportKeysListFragment extends ListFragment implements
}
public void loadNew(LoaderState loaderState) {
-
mLoaderState = loaderState;
+ if (mLoaderState instanceof BytesLoaderState) {
+ BytesLoaderState ls = (BytesLoaderState) mLoaderState;
+
+ if ( ls.mDataUri != null && ! checkAndRequestReadPermission(ls.mDataUri)) {
+ return;
+ }
+ }
+
restartLoaders();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
index af60a1d9b..37e01e98f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
@@ -90,8 +90,9 @@ public class MainActivity extends BaseSecurityTokenNfcActivity implements FabCon
@Override
public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
if (drawerItem != null) {
+ PrimaryDrawerItem item = (PrimaryDrawerItem) drawerItem;
Intent intent = null;
- switch (drawerItem.getIdentifier()) {
+ switch ((int) item.getIdentifier()) {
case ID_KEYS:
onKeysSelected();
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiUserIdsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiUserIdsFragment.java
new file mode 100644
index 000000000..8ba695cf7
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiUserIdsFragment.java
@@ -0,0 +1,223 @@
+package org.sufficientlysecure.keychain.ui;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
+import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
+
+public class MultiUserIdsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{
+ public static final String ARG_CHECK_STATES = "check_states";
+ public static final String EXTRA_KEY_IDS = "extra_key_ids";
+ private boolean checkboxVisibility = true;
+
+ ListView mUserIds;
+ private MultiUserIdsAdapter mUserIdsAdapter;
+
+ private long[] mPubMasterKeyIds;
+
+ public static final String[] USER_IDS_PROJECTION = new String[]{
+ KeychainContract.UserPackets._ID,
+ KeychainContract.UserPackets.MASTER_KEY_ID,
+ KeychainContract.UserPackets.USER_ID,
+ KeychainContract.UserPackets.IS_PRIMARY,
+ KeychainContract.UserPackets.IS_REVOKED
+ };
+ private static final int INDEX_MASTER_KEY_ID = 1;
+ private static final int INDEX_USER_ID = 2;
+ @SuppressWarnings("unused")
+ private static final int INDEX_IS_PRIMARY = 3;
+ @SuppressWarnings("unused")
+ private static final int INDEX_IS_REVOKED = 4;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.multi_user_ids_fragment, null);
+
+ mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mPubMasterKeyIds = getActivity().getIntent().getLongArrayExtra(EXTRA_KEY_IDS);
+ if (mPubMasterKeyIds == null) {
+ Log.e(Constants.TAG, "List of key ids to certify missing!");
+ getActivity().finish();
+ return;
+ }
+
+ ArrayList<Boolean> checkedStates = null;
+ if (savedInstanceState != null) {
+ checkedStates = (ArrayList<Boolean>) savedInstanceState.getSerializable(ARG_CHECK_STATES);
+ }
+
+ mUserIdsAdapter = new MultiUserIdsAdapter(getActivity(), null, 0, checkedStates, checkboxVisibility);
+ mUserIds.setAdapter(mUserIdsAdapter);
+ mUserIds.setDividerHeight(0);
+
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ ArrayList<Boolean> states = mUserIdsAdapter.getCheckStates();
+ // no proper parceling method available :(
+ outState.putSerializable(ARG_CHECK_STATES, states);
+ }
+
+ public ArrayList<CertifyActionsParcel.CertifyAction> getSelectedCertifyActions() {
+ if (!checkboxVisibility) {
+ throw new AssertionError("Item selection not allowed");
+ }
+
+ return mUserIdsAdapter.getSelectedCertifyActions();
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ Uri uri = KeychainContract.UserPackets.buildUserIdsUri();
+
+ String selection, ids[];
+ {
+ // generate placeholders and string selection args
+ ids = new String[mPubMasterKeyIds.length];
+ StringBuilder placeholders = new StringBuilder("?");
+ for (int i = 0; i < mPubMasterKeyIds.length; i++) {
+ ids[i] = Long.toString(mPubMasterKeyIds[i]);
+ if (i != 0) {
+ placeholders.append(",?");
+ }
+ }
+ // put together selection string
+ selection = KeychainContract.UserPackets.IS_REVOKED + " = 0" + " AND "
+ + KeychainDatabase.Tables.USER_PACKETS + "." + KeychainContract.UserPackets.MASTER_KEY_ID
+ + " IN (" + placeholders + ")";
+ }
+
+ return new CursorLoader(getActivity(), uri,
+ USER_IDS_PROJECTION, selection, ids,
+ KeychainDatabase.Tables.USER_PACKETS + "." + KeychainContract.UserPackets.MASTER_KEY_ID + " ASC"
+ + ", " + KeychainDatabase.Tables.USER_PACKETS + "." + KeychainContract.UserPackets.USER_ID + " ASC"
+ );
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+
+ MatrixCursor matrix = new MatrixCursor(new String[]{
+ "_id", "user_data", "grouped"
+ }) {
+ @Override
+ public byte[] getBlob(int column) {
+ return super.getBlob(column);
+ }
+ };
+ data.moveToFirst();
+
+ long lastMasterKeyId = 0;
+ String lastName = "";
+ ArrayList<String> uids = new ArrayList<>();
+
+ boolean header = true;
+
+ // Iterate over all rows
+ while (!data.isAfterLast()) {
+ long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
+ String userId = data.getString(INDEX_USER_ID);
+ KeyRing.UserId pieces = KeyRing.splitUserId(userId);
+
+ // Two cases:
+
+ boolean grouped = masterKeyId == lastMasterKeyId;
+ boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces.name);
+ // Remember for next loop
+ lastName = pieces.name;
+
+ Log.d(Constants.TAG, Long.toString(masterKeyId, 16) + (grouped ? "grouped" : "not grouped"));
+
+ if (!subGrouped) {
+ // 1. This name should NOT be grouped with the previous, so we flush the buffer
+
+ Parcel p = Parcel.obtain();
+ p.writeStringList(uids);
+ byte[] d = p.marshall();
+ p.recycle();
+
+ matrix.addRow(new Object[]{
+ lastMasterKeyId, d, header ? 1 : 0
+ });
+ // indicate that we have a header for this masterKeyId
+ header = false;
+
+ // Now clear the buffer, and add the new user id, for the next round
+ uids.clear();
+
+ }
+
+ // 2. This name should be grouped with the previous, just add to buffer
+ uids.add(userId);
+ lastMasterKeyId = masterKeyId;
+
+ // If this one wasn't grouped, the next one's gotta be a header
+ if (!grouped) {
+ header = true;
+ }
+
+ // Regardless of the outcome, move to next entry
+ data.moveToNext();
+
+ }
+
+ // If there is anything left in the buffer, flush it one last time
+ if (!uids.isEmpty()) {
+
+ Parcel p = Parcel.obtain();
+ p.writeStringList(uids);
+ byte[] d = p.marshall();
+ p.recycle();
+
+ matrix.addRow(new Object[]{
+ lastMasterKeyId, d, header ? 1 : 0
+ });
+
+ }
+
+ mUserIdsAdapter.swapCursor(matrix);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mUserIdsAdapter.swapCursor(null);
+ }
+
+ public void setCheckboxVisibility(boolean checkboxVisibility) {
+ this.checkboxVisibility = checkboxVisibility;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
index fd4f27176..2c562c30e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
@@ -46,8 +46,6 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewAnimator;
-import com.github.pinball83.maskededittext.MaskedEditText;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
@@ -60,7 +58,6 @@ import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
-import org.sufficientlysecure.keychain.remote.CryptoInputParcelCacheService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@@ -157,7 +154,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
public static class PassphraseDialogFragment extends DialogFragment implements TextView.OnEditorActionListener {
private EditText mPassphraseEditText;
private TextView mPassphraseText;
- private MaskedEditText mBackupCodeEditText;
+ private EditText[] mBackupCodeEditText;
private boolean mIsCancelled = false;
private RequiredInputParcel mRequiredInput;
@@ -184,13 +181,15 @@ public class PassphraseDialogActivity extends FragmentActivity {
View view = inflater.inflate(R.layout.passphrase_dialog_backup_code, null);
alert.setView(view);
- mBackupCodeEditText = (MaskedEditText) view.findViewById(R.id.backup_code);
- // NOTE: order of these method calls matter, see setupAutomaticLinebreak()
- mBackupCodeEditText.setInputType(
- InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
- setupAutomaticLinebreak(mBackupCodeEditText);
- mBackupCodeEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
- mBackupCodeEditText.setOnEditorActionListener(this);
+ mBackupCodeEditText = new EditText[6];
+ mBackupCodeEditText[0] = (EditText) view.findViewById(R.id.backup_code_1);
+ mBackupCodeEditText[1] = (EditText) view.findViewById(R.id.backup_code_2);
+ mBackupCodeEditText[2] = (EditText) view.findViewById(R.id.backup_code_3);
+ mBackupCodeEditText[3] = (EditText) view.findViewById(R.id.backup_code_4);
+ mBackupCodeEditText[4] = (EditText) view.findViewById(R.id.backup_code_5);
+ mBackupCodeEditText[5] = (EditText) view.findViewById(R.id.backup_code_6);
+
+ setupEditTextFocusNext(mBackupCodeEditText);
AlertDialog dialog = alert.create();
dialog.setButton(DialogInterface.BUTTON_POSITIVE,
@@ -281,28 +280,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
mPassphraseText.setText(message);
mPassphraseEditText.setHint(hint);
- // Hack to open keyboard.
- // This is the only method that I found to work across all Android versions
- // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
- // Notes: * onCreateView can't be used because we want to add buttons to the dialog
- // * opening in onActivityCreated does not work on Android 4.4
- mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mPassphraseEditText.post(new Runnable() {
- @Override
- public void run() {
- if (getActivity() == null || mPassphraseEditText == null) {
- return;
- }
- InputMethodManager imm = (InputMethodManager) getActivity()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
- }
- });
- }
- });
- mPassphraseEditText.requestFocus();
+ openKeyboard(mPassphraseEditText);
mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
mPassphraseEditText.setOnEditorActionListener(this);
@@ -325,17 +303,62 @@ public class PassphraseDialogActivity extends FragmentActivity {
}
/**
- * Automatic line break with max 6 lines for smaller displays
- * <p/>
- * NOTE: I was not able to get this behaviour using XML!
- * Looks like the order of these method calls matter, see http://stackoverflow.com/a/11171307
+ * Hack to open keyboard.
+ * This is the only method that I found to work across all Android versions
+ * http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
+ * Notes:
+ * * onCreateView can't be used because we want to add buttons to the dialog
+ * * opening in onActivityCreated does not work on Android 4.4
*/
- private void setupAutomaticLinebreak(TextView textview) {
- textview.setSingleLine(true);
- textview.setMaxLines(6);
- textview.setHorizontallyScrolling(false);
+ private void openKeyboard(final TextView textView) {
+ textView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ textView.post(new Runnable() {
+ @Override
+ public void run() {
+ if (getActivity() == null || textView == null) {
+ return;
+ }
+ InputMethodManager imm = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(textView, InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+ }
+ });
+ textView.requestFocus();
+ }
+
+ private static void setupEditTextFocusNext(final EditText[] backupCodes) {
+ for (int i = 0; i < backupCodes.length - 1; i++) {
+
+ final int next = i + 1;
+
+ backupCodes[i].addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ boolean inserting = before < count;
+ boolean cursorAtEnd = (start + count) == 4;
+
+ if (inserting && cursorAtEnd) {
+ backupCodes[next].requestFocus();
+ }
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ });
+
+ }
}
+
@Override
public void onStart() {
super.onStart();
@@ -347,8 +370,17 @@ public class PassphraseDialogActivity extends FragmentActivity {
public void onClick(View v) {
if (mRequiredInput.mType == RequiredInputType.BACKUP_CODE) {
- Passphrase passphrase =
- new Passphrase(mBackupCodeEditText.getText().toString());
+ StringBuilder backupCodeInput = new StringBuilder(26);
+ for (EditText editText : mBackupCodeEditText) {
+ if (editText.getText().length() < 4) {
+ return;
+ }
+ backupCodeInput.append(editText.getText());
+ backupCodeInput.append('-');
+ }
+ backupCodeInput.deleteCharAt(backupCodeInput.length() - 1);
+
+ Passphrase passphrase = new Passphrase(backupCodeInput.toString());
finishCaching(passphrase);
return;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
index 78d82d436..925ad19d4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
@@ -3,6 +3,7 @@
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2013-2014 Signe Rüsch
* Copyright (C) 2013-2014 Philipp Jakubeit
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
*
* 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
@@ -35,10 +36,12 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.securitytoken.KeyType;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenNfcActivity;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.OrientationUtils;
@@ -69,8 +72,6 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
private RequiredInputParcel mRequiredInput;
- private static final byte[] BLANK_FINGERPRINT = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
private CryptoInputParcel mInputParcel;
@Override
@@ -137,9 +138,33 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
private void obtainPassphraseIfRequired() {
// obtain passphrase for this subkey
- if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_MOVE_KEY_TO_CARD
- && mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_RESET_CARD) {
+ if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD
+ && mRequiredInput.mType != RequiredInputParcel.RequiredInputType.SECURITY_TOKEN_RESET_CARD) {
obtainSecurityTokenPin(mRequiredInput);
+ checkPinAvailability();
+ } else {
+ checkDeviceConnection();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (REQUEST_CODE_PIN == requestCode) {
+ checkPinAvailability();
+ }
+ }
+
+ private void checkPinAvailability() {
+ try {
+ Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this,
+ mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
+ if (passphrase != null) {
+ checkDeviceConnection();
+ }
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new AssertionError(
+ "tried to find passphrase for non-existing key. this is a programming error!");
}
}
@@ -149,39 +174,53 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
}
@Override
- public void onNfcPreExecute() {
+ public void onSecurityTokenPreExecute() {
// start with indeterminate progress
vAnimator.setDisplayedChild(1);
nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.TRANSFERRING);
}
@Override
- protected void doNfcInBackground() throws IOException {
+ protected void doSecurityTokenInBackground() throws IOException {
switch (mRequiredInput.mType) {
- case NFC_DECRYPT: {
+ case SECURITY_TOKEN_DECRYPT: {
+ long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(
+ mSecurityTokenHelper.getKeyFingerprint(KeyType.ENCRYPT));
+
+ if (tokenKeyId != mRequiredInput.getSubKeyId()) {
+ throw new IOException(getString(R.string.error_wrong_security_token));
+ }
+
for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] encryptedSessionKey = mRequiredInput.mInputData[i];
- byte[] decryptedSessionKey = nfcDecryptSessionKey(encryptedSessionKey);
+ byte[] decryptedSessionKey = mSecurityTokenHelper.decryptSessionKey(encryptedSessionKey);
mInputParcel.addCryptoData(encryptedSessionKey, decryptedSessionKey);
}
break;
}
- case NFC_SIGN: {
+ case SECURITY_TOKEN_SIGN: {
+ long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(
+ mSecurityTokenHelper.getKeyFingerprint(KeyType.SIGN));
+
+ if (tokenKeyId != mRequiredInput.getSubKeyId()) {
+ throw new IOException(getString(R.string.error_wrong_security_token));
+ }
+
mInputParcel.addSignatureTime(mRequiredInput.mSignatureTime);
for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] hash = mRequiredInput.mInputData[i];
int algo = mRequiredInput.mSignAlgos[i];
- byte[] signedHash = nfcCalculateSignature(hash, algo);
+ byte[] signedHash = mSecurityTokenHelper.calculateSignature(hash, algo);
mInputParcel.addCryptoData(hash, signedHash);
}
break;
}
- case NFC_MOVE_KEY_TO_CARD: {
+ case SECURITY_TOKEN_MOVE_KEY_TO_CARD: {
// TODO: assume PIN and Admin PIN to be default for this operation
- mPin = new Passphrase("123456");
- mAdminPin = new Passphrase("12345678");
+ mSecurityTokenHelper.setPin(new Passphrase("123456"));
+ mSecurityTokenHelper.setAdminPin(new Passphrase("12345678"));
ProviderHelper providerHelper = new ProviderHelper(this);
CanonicalizedSecretKeyRing secretKeyRing;
@@ -202,11 +241,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
long subkeyId = buf.getLong();
CanonicalizedSecretKey key = secretKeyRing.getSecretKey(subkeyId);
-
- long keyGenerationTimestampMillis = key.getCreationTime().getTime();
- long keyGenerationTimestamp = keyGenerationTimestampMillis / 1000;
- byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) keyGenerationTimestamp).array();
- byte[] tokenSerialNumber = Arrays.copyOf(nfcGetAid(), 16);
+ byte[] tokenSerialNumber = Arrays.copyOf(mSecurityTokenHelper.getAid(), 16);
Passphrase passphrase;
try {
@@ -216,46 +251,20 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
throw new IOException("Unable to get cached passphrase!");
}
- if (key.canSign() || key.canCertify()) {
- if (shouldPutKey(key.getFingerprint(), 0)) {
- nfcPutKey(0xB6, key, passphrase);
- nfcPutData(0xCE, timestampBytes);
- nfcPutData(0xC7, key.getFingerprint());
- } else {
- throw new IOException("Key slot occupied; token must be reset to put new signature key.");
- }
- } else if (key.canEncrypt()) {
- if (shouldPutKey(key.getFingerprint(), 1)) {
- nfcPutKey(0xB8, key, passphrase);
- nfcPutData(0xCF, timestampBytes);
- nfcPutData(0xC8, key.getFingerprint());
- } else {
- throw new IOException("Key slot occupied; token must be reset to put new decryption key.");
- }
- } else if (key.canAuthenticate()) {
- if (shouldPutKey(key.getFingerprint(), 2)) {
- nfcPutKey(0xA4, key, passphrase);
- nfcPutData(0xD0, timestampBytes);
- nfcPutData(0xC9, key.getFingerprint());
- } else {
- throw new IOException("Key slot occupied; token must be reset to put new authentication key.");
- }
- } else {
- throw new IOException("Inappropriate key flags for Security Token key.");
- }
+ mSecurityTokenHelper.changeKey(key, passphrase);
// TODO: Is this really used anywhere?
mInputParcel.addCryptoData(subkeyBytes, tokenSerialNumber);
}
// change PINs afterwards
- nfcModifyPIN(0x81, newPin);
- nfcModifyPIN(0x83, newAdminPin);
+ mSecurityTokenHelper.modifyPin(0x81, newPin);
+ mSecurityTokenHelper.modifyPin(0x83, newAdminPin);
break;
}
- case NFC_RESET_CARD: {
- nfcResetCard();
+ case SECURITY_TOKEN_RESET_CARD: {
+ mSecurityTokenHelper.resetAndWipeToken();
break;
}
@@ -267,7 +276,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
}
@Override
- protected final void onNfcPostExecute() {
+ protected final void onSecurityTokenPostExecute() {
handleResult(mInputParcel);
// show finish
@@ -275,28 +284,33 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.DONE);
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- // check all 200ms if Security Token has been taken away
- while (true) {
- if (isNfcConnected()) {
- try {
- Thread.sleep(200);
- } catch (InterruptedException ignored) {
+ if (mSecurityTokenHelper.isPersistentConnectionAllowed()) {
+ // Just close
+ finish();
+ } else {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // check all 200ms if Security Token has been taken away
+ while (true) {
+ if (isSecurityTokenConnected()) {
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ignored) {
+ }
+ } else {
+ return null;
}
- } else {
- return null;
}
}
- }
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- finish();
- }
- }.execute();
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ finish();
+ }
+ }.execute();
+ }
}
/**
@@ -311,7 +325,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
}
@Override
- protected void onNfcError(String error) {
+ protected void onSecurityTokenError(String error) {
pauseTagHandling();
vErrorText.setText(error + "\n\n" + getString(R.string.security_token_nfc_try_again_text));
@@ -321,31 +335,11 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
}
@Override
- public void onNfcPinError(String error) {
- onNfcError(error);
+ public void onSecurityTokenPinError(String error) {
+ onSecurityTokenError(error);
// clear (invalid) passphrase
PassphraseCacheService.clearCachedPassphrase(
this, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
}
-
- private boolean shouldPutKey(byte[] fingerprint, int idx) throws IOException {
- byte[] tokenFingerprint = nfcGetMasterKeyFingerprint(idx);
-
- // Note: special case: This should not happen, but happens with
- // https://github.com/FluffyKaon/OpenPGP-Card, thus for now assume true
- if (tokenFingerprint == null) {
- return true;
- }
-
- // Slot is empty, or contains this key already. PUT KEY operation is safe
- if (Arrays.equals(tokenFingerprint, BLANK_FINGERPRINT) ||
- Arrays.equals(tokenFingerprint, fingerprint)) {
- return true;
- }
-
- // Slot already contains a different key; don't overwrite it.
- return false;
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
index ea70cde2a..4fd327c8f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
@@ -19,8 +19,6 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.List;
-
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -49,6 +47,7 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.KeychainApplication;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
@@ -59,6 +58,8 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+import java.util.List;
+
public class SettingsActivity extends AppCompatPreferenceActivity {
public static final int REQUEST_CODE_KEYSERVER_PREF = 0x00007005;
@@ -405,7 +406,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
}
/**
- * This fragment shows the keyserver/contacts sync preferences
+ * This fragment shows the keyserver/wifi-only-sync/contacts sync preferences
*/
public static class SyncPrefsFragment extends PresetPreferenceFragment {
@@ -422,8 +423,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
super.onResume();
// this needs to be done in onResume since the user can change sync values from Android
// settings and we need to reflect that change when the user navigates back
- AccountManager manager = AccountManager.get(getActivity());
- final Account account = manager.getAccountsByType(Constants.ACCOUNT_TYPE)[0];
+ final Account account = KeychainApplication.createAccountIfNecessary(getActivity());
// for keyserver sync
initializeSyncCheckBox(
(SwitchPreference) findPreference(Constants.Pref.SYNC_KEYSERVER),
@@ -441,8 +441,11 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
private void initializeSyncCheckBox(final SwitchPreference syncCheckBox,
final Account account,
final String authority) {
- boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority)
- && checkContactsPermission(authority);
+ // account is null if it could not be created for some reason
+ boolean syncEnabled =
+ account != null
+ && ContentResolver.getSyncAutomatically(account, authority)
+ && checkContactsPermission(authority);
syncCheckBox.setChecked(syncEnabled);
setSummary(syncCheckBox, authority, syncEnabled);
@@ -464,6 +467,11 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
return false;
}
} else {
+ if (account == null) {
+ // if account could not be created for some reason,
+ // we can't have our sync
+ return false;
+ }
// disable syncs
ContentResolver.setSyncAutomatically(account, authority, false);
// immediately delete any linked contacts
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java
index 5a8ab36bc..488558aa3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java
@@ -40,6 +40,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.dialog.AddEditKeyserverDialogFragment;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperAdapter;
import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperViewHolder;
import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperDragCallback;
@@ -312,19 +313,19 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
public void showAsSelectedKeyserver() {
isSelectedKeyserver = true;
selectedServerLabel.setVisibility(View.VISIBLE);
- outerLayout.setBackgroundColor(getResources().getColor(R.color.android_green_dark));
+ outerLayout.setBackgroundColor(FormattingUtils.getColorFromAttr(getContext(), R.attr.colorPrimaryDark));
}
public void showAsUnselectedKeyserver() {
isSelectedKeyserver = false;
selectedServerLabel.setVisibility(View.GONE);
- outerLayout.setBackgroundColor(Color.WHITE);
+ outerLayout.setBackgroundColor(0);
}
@Override
public void onItemSelected() {
selectedServerLabel.setVisibility(View.GONE);
- itemView.setBackgroundColor(Color.LTGRAY);
+ itemView.setBackgroundColor(FormattingUtils.getColorFromAttr(getContext(), R.attr.colorBrightToolbar));
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index f38e4928d..306b022c1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -31,10 +31,7 @@ import android.widget.Spinner;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.UploadResult;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.UploadKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
@@ -53,7 +50,6 @@ public class UploadKeyActivity extends BaseActivity
// CryptoOperationHelper.Callback vars
private String mKeyserver;
- private long mMasterKeyId;
private CryptoOperationHelper<UploadKeyringParcel, UploadResult> mUploadOpHelper;
@Override
@@ -63,6 +59,10 @@ public class UploadKeyActivity extends BaseActivity
mUploadButton = findViewById(R.id.upload_key_action_upload);
mKeyServerSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
+ MultiUserIdsFragment mMultiUserIdsFragment = (MultiUserIdsFragment)
+ getSupportFragmentManager().findFragmentById(R.id.multi_user_ids_fragment);
+ mMultiUserIdsFragment.setCheckboxVisibility(false);
+
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
.getKeyServers()
@@ -89,15 +89,6 @@ public class UploadKeyActivity extends BaseActivity
return;
}
- try {
- mMasterKeyId = new ProviderHelper(this).getCachedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingUri(mDataUri)).getMasterKeyId();
- } catch (PgpKeyNotFoundException e) {
- Log.e(Constants.TAG, "Intent data pointed to bad key!");
- finish();
- return;
- }
-
}
@Override
@@ -136,7 +127,9 @@ public class UploadKeyActivity extends BaseActivity
@Override
public UploadKeyringParcel createOperationInput() {
- return new UploadKeyringParcel(mKeyserver, mMasterKeyId);
+ long[] masterKeyIds = getIntent().getLongArrayExtra(MultiUserIdsFragment.EXTRA_KEY_IDS);
+
+ return new UploadKeyringParcel(mKeyserver, masterKeyIds[0]);
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java
new file mode 100644
index 000000000..05b30b1ae
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class UsbEventReceiverActivity extends Activity {
+ public static final String ACTION_USB_PERMISSION =
+ "org.sufficientlysecure.keychain.ui.USB_PERMISSION";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
+
+ Intent intent = getIntent();
+ if (intent != null) {
+ if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+ UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+
+ Log.d(Constants.TAG, "Requesting permission for " + usbDevice.getDeviceName());
+ usbManager.requestPermission(usbDevice,
+ PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0));
+ }
+ }
+
+ // Close the activity
+ finish();
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 03fc07936..e47ca1db9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -32,6 +32,7 @@ import android.app.ActivityOptions;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.AsyncTask;
@@ -170,9 +171,9 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
private byte[] mFingerprint;
private String mFingerprintString;
- private byte[] mNfcFingerprints;
- private String mNfcUserId;
- private byte[] mNfcAid;
+ private byte[] mSecurityTokenFingerprints;
+ private String mSecurityTokenUserId;
+ private byte[] mSecurityTokenAid;
@SuppressLint("InflateParams")
@Override
@@ -469,8 +470,7 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
// use new passphrase!
mSaveKeyringParcel.mNewUnlock = new SaveKeyringParcel.ChangeUnlockParcel(
- (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
- null
+ (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE)
);
mEditOpHelper.cryptoOperation();
@@ -646,17 +646,17 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
}
@Override
- protected void doNfcInBackground() throws IOException {
+ protected void doSecurityTokenInBackground() throws IOException {
- mNfcFingerprints = nfcGetFingerprints();
- mNfcUserId = nfcGetUserId();
- mNfcAid = nfcGetAid();
+ mSecurityTokenFingerprints = mSecurityTokenHelper.getFingerprints();
+ mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
+ mSecurityTokenAid = mSecurityTokenHelper.getAid();
}
@Override
- protected void onNfcPostExecute() {
+ protected void onSecurityTokenPostExecute() {
- long tokenId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
+ long tokenId = KeyFormattingUtils.getKeyIdFromFingerprint(mSecurityTokenFingerprints);
try {
@@ -667,7 +667,7 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
// if the master key of that key matches this one, just show the token dialog
if (KeyFormattingUtils.convertFingerprintToHex(candidateFp).equals(mFingerprintString)) {
- showSecurityTokenFragment(mNfcFingerprints, mNfcUserId, mNfcAid);
+ showSecurityTokenFragment(mSecurityTokenFingerprints, mSecurityTokenUserId, mSecurityTokenAid);
return;
}
@@ -680,9 +680,9 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
Intent intent = new Intent(
ViewKeyActivity.this, ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mNfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mNfcFingerprints);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
finish();
}
@@ -695,9 +695,9 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
public void onAction() {
Intent intent = new Intent(
ViewKeyActivity.this, CreateKeyActivity.class);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mNfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mNfcFingerprints);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
finish();
}
@@ -924,6 +924,7 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
}
mPhoto.setImageBitmap(photo);
+ mPhoto.setColorFilter(getResources().getColor(R.color.toolbar_photo_tint), PorterDuff.Mode.SRC_ATOP);
mPhotoLayout.setVisibility(View.VISIBLE);
}
};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
index ce2f2def8..02eae1b2b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
@@ -238,7 +238,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
// let user choose application
Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.setType("text/plain");
+ sendIntent.setType(Constants.MIME_TYPE_KEYS);
// NOTE: Don't use Intent.EXTRA_TEXT to send the key
// better send it via a Uri!
@@ -455,8 +455,19 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
}
private void uploadToKeyserver() {
+ long keyId;
+ try {
+ keyId = new ProviderHelper(getActivity())
+ .getCachedPublicKeyRing(mDataUri)
+ .extractOrGetMasterKeyId();
+ } catch (PgpKeyNotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ Notify.create(getActivity(), "key not found", Style.ERROR).show();
+ return;
+ }
Intent uploadIntent = new Intent(getActivity(), UploadKeyActivity.class);
uploadIntent.setData(mDataUri);
+ uploadIntent.putExtra(MultiUserIdsFragment.EXTRA_KEY_IDS, new long[]{keyId});
startActivityForResult(uploadIntent, 0);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
index fc6db1b92..93b38af9b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
@@ -39,6 +39,7 @@ import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ViewAnimator;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
@@ -346,50 +347,45 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
}
break;
}
- case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_CARD: {
- // TODO: enable later when Admin PIN handling is resolved
- Notify.create(getActivity(),
- "This feature will be available in an upcoming OpenKeychain version.",
- Notify.Style.WARN).show();
- break;
+ case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_SECURITY_TOKEN: {
+ SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
+ if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
+ secretKeyType == SecretKeyType.GNU_DUMMY) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_stripped, Notify.Style.ERROR)
+ .show();
+ break;
+ }
+
+ int algorithm = mSubkeysAdapter.getAlgorithm(position);
+ if (algorithm != PublicKeyAlgorithmTags.RSA_GENERAL
+ && algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT
+ && algorithm != PublicKeyAlgorithmTags.RSA_SIGN) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_algo, Notify.Style.ERROR)
+ .show();
+ break;
+ }
+
+ if (mSubkeysAdapter.getKeySize(position) != 2048) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_size, Notify.Style.ERROR)
+ .show();
+ break;
+ }
-// Activity activity = EditKeyFragment.this.getActivity();
-// SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
-// if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
-// secretKeyType == SecretKeyType.GNU_DUMMY) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_stripped, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-// int algorithm = mSubkeysAdapter.getAlgorithm(position);
-// // these are the PGP constants for RSA_GENERAL, RSA_ENCRYPT and RSA_SIGN
-// if (algorithm != 1 && algorithm != 2 && algorithm != 3) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_algo, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-// if (mSubkeysAdapter.getKeySize(position) != 2048) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_size, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-//
-//
-// SubkeyChange change;
-// change = mSaveKeyringParcel.getSubkeyChange(keyId);
-// if (change == null) {
-// mSaveKeyringParcel.mChangeSubKeys.add(
-// new SubkeyChange(keyId, false, true)
-// );
-// break;
-// }
-// // toggle
-// change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
-// if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
-// // User had chosen to strip key, but now wants to divert it.
-// change.mDummyStrip = false;
-// }
-// break;
+ SubkeyChange change;
+ change = mEditModeSaveKeyringParcel.getSubkeyChange(keyId);
+ if (change == null) {
+ mEditModeSaveKeyringParcel.mChangeSubKeys.add(
+ new SubkeyChange(keyId, false, true)
+ );
+ break;
+ }
+ // toggle
+ change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
+ if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
+ // User had chosen to strip key, but now wants to divert it.
+ change.mDummyStrip = false;
+ }
+ break;
}
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
index fb72a263e..4a68c55fe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
@@ -333,14 +333,6 @@ public class KeyAdapter extends CursorAdapter {
return mUserId.email;
}
}
-
- // TODO: workaround for bug in TokenAutoComplete,
- // see https://github.com/open-keychain/open-keychain/issues/1636
- @Override
- public String toString() {
- return " ";
- }
-
}
public static String[] getProjectionWith(String[] projection) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
index b91abf076..d247faddc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
@@ -39,6 +39,7 @@ import java.util.ArrayList;
public class MultiUserIdsAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private final ArrayList<Boolean> mCheckStates;
+ private boolean checkboxVisibility = true;
public MultiUserIdsAdapter(Context context, Cursor c, int flags, ArrayList<Boolean> preselectStates) {
super(context, c, flags);
@@ -46,6 +47,11 @@ public class MultiUserIdsAdapter extends CursorAdapter {
mCheckStates = preselectStates == null ? new ArrayList<Boolean>() : preselectStates;
}
+ public MultiUserIdsAdapter(Context context, Cursor c, int flags, ArrayList<Boolean> preselectStates, boolean checkboxVisibility) {
+ this(context,c,flags,preselectStates);
+ this.checkboxVisibility = checkboxVisibility;
+ }
+
@Override
public Cursor swapCursor(Cursor newCursor) {
if (newCursor != null) {
@@ -138,6 +144,7 @@ public class MultiUserIdsAdapter extends CursorAdapter {
}
});
vCheckBox.setClickable(false);
+ vCheckBox.setVisibility(checkboxVisibility?View.VISIBLE:View.GONE);
View vUidBody = view.findViewById(R.id.user_id_body);
vUidBody.setClickable(true);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
index 8b2481c29..717299b55 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.app.Activity;
import android.content.Context;
import android.graphics.Typeface;
+import android.support.v4.app.FragmentActivity;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
@@ -32,6 +33,7 @@ import android.widget.TextView;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.util.Calendar;
@@ -65,10 +67,11 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
public SaveKeyringParcel.SubkeyAdd mModel;
}
+
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// Not recycled, inflate a new view
- convertView = mInflater.inflate(R.layout.view_key_adv_subkey_item, null);
+ convertView = mInflater.inflate(R.layout.view_key_adv_subkey_item, parent, false);
final ViewHolder holder = new ViewHolder();
holder.vKeyId = (TextView) convertView.findViewById(R.id.subkey_item_key_id);
holder.vKeyDetails = (TextView) convertView.findViewById(R.id.subkey_item_details);
@@ -88,16 +91,8 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
vStatus.setVisibility(View.GONE);
convertView.setTag(holder);
-
- holder.vDelete.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // remove reference model item from adapter (data and notify about change)
- SubkeysAddedAdapter.this.remove(holder.mModel);
- }
- });
-
}
+
final ViewHolder holder = (ViewHolder) convertView.getTag();
// save reference to model item
@@ -113,8 +108,41 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
boolean isMasterKey = mNewKeyring && position == 0;
if (isMasterKey) {
holder.vKeyId.setTypeface(null, Typeface.BOLD);
+ holder.vDelete.setImageResource(R.drawable.ic_change_grey_24dp);
+ holder.vDelete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // swapping out the old master key with newly set master key
+ AddSubkeyDialogFragment addSubkeyDialogFragment =
+ AddSubkeyDialogFragment.newInstance(true);
+ addSubkeyDialogFragment
+ .setOnAlgorithmSelectedListener(
+ new AddSubkeyDialogFragment.OnAlgorithmSelectedListener() {
+ @Override
+ public void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey) {
+ // calculate manually as the provided position variable
+ // is not always accurate
+ int pos = SubkeysAddedAdapter.this.getPosition(holder.mModel);
+ SubkeysAddedAdapter.this.remove(holder.mModel);
+ SubkeysAddedAdapter.this.insert(newSubkey, pos);
+ }
+ }
+ );
+ addSubkeyDialogFragment.show(
+ ((FragmentActivity)mActivity).getSupportFragmentManager()
+ , "addSubkeyDialog");
+ }
+ });
} else {
holder.vKeyId.setTypeface(null, Typeface.NORMAL);
+ holder.vDelete.setImageResource(R.drawable.ic_close_grey_24dp);
+ holder.vDelete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // remove reference model item from adapter (data and notify about change)
+ SubkeysAddedAdapter.this.remove(holder.mModel);
+ }
+ });
}
holder.vKeyId.setText(R.string.edit_key_new_subkey);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java
index 107c63e0b..063181dfe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java
@@ -26,6 +26,7 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -45,8 +46,8 @@ public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- initTheme();
super.onCreate(savedInstanceState);
+ initTheme();
initLayout();
initToolbar();
}
@@ -65,6 +66,16 @@ public abstract class BaseActivity extends AppCompatActivity {
}
}
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home :
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
public static void onResumeChecks(Context context) {
KeyserverSyncAdapterService.cancelUpdates(context);
// in case user has disabled sync from Android account settings
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java
index c3352363a..f4c0a9365 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java
@@ -3,6 +3,7 @@
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2013-2014 Signe Rüsch
* Copyright (C) 2013-2014 Philipp Jakubeit
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
*
* 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,44 +21,33 @@
package org.sufficientlysecure.keychain.ui.base;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.security.interfaces.RSAPrivateCrtKey;
-
import android.app.Activity;
-import android.app.PendingIntent;
+import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.TagLostException;
-import android.nfc.tech.IsoDep;
import android.os.AsyncTask;
import android.os.Bundle;
-import nordpol.Apdu;
-import nordpol.android.TagDispatcher;
-import nordpol.android.AndroidCard;
-import nordpol.android.OnDiscoveredTagListener;
-import nordpol.IsoCard;
-
-import org.bouncycastle.bcpg.HashAlgorithmTags;
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService.KeyNotFoundException;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import org.sufficientlysecure.keychain.securitytoken.CardException;
+import org.sufficientlysecure.keychain.securitytoken.NfcTransport;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenHelper;
+import org.sufficientlysecure.keychain.securitytoken.Transport;
+import org.sufficientlysecure.keychain.util.UsbConnectionDispatcher;
+import org.sufficientlysecure.keychain.securitytoken.UsbTransport;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
@@ -66,56 +56,52 @@ import org.sufficientlysecure.keychain.ui.dialog.FidesmoPgpInstallDialog;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
-import org.sufficientlysecure.keychain.util.Iso7816TLV;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
-public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implements OnDiscoveredTagListener {
+import java.io.IOException;
+
+import nordpol.android.OnDiscoveredTagListener;
+import nordpol.android.TagDispatcher;
+
+public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
+ implements OnDiscoveredTagListener, UsbConnectionDispatcher.OnDiscoveredUsbDeviceListener {
public static final int REQUEST_CODE_PIN = 1;
public static final String EXTRA_TAG_HANDLING_ENABLED = "tag_handling_enabled";
- // Fidesmo constants
- private static final String FIDESMO_APPS_AID_PREFIX = "A000000617";
private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android";
- protected Passphrase mPin;
- protected Passphrase mAdminPin;
- protected boolean mPw1ValidForMultipleSignatures;
- protected boolean mPw1ValidatedForSignature;
- protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming?
- protected boolean mPw3Validated;
+ protected SecurityTokenHelper mSecurityTokenHelper = SecurityTokenHelper.getInstance();
protected TagDispatcher mTagDispatcher;
- private IsoCard mIsoCard;
+ protected UsbConnectionDispatcher mUsbDispatcher;
private boolean mTagHandlingEnabled;
- private static final int TIMEOUT = 100000;
-
- private byte[] mNfcFingerprints;
- private String mNfcUserId;
- private byte[] mNfcAid;
+ private byte[] mSecurityTokenFingerprints;
+ private String mSecurityTokenUserId;
+ private byte[] mSecurityTokenAid;
/**
- * Override to change UI before NFC handling (UI thread)
+ * Override to change UI before SecurityToken handling (UI thread)
*/
- protected void onNfcPreExecute() {
+ protected void onSecurityTokenPreExecute() {
}
/**
- * Override to implement NFC operations (background thread)
+ * Override to implement SecurityToken operations (background thread)
*/
- protected void doNfcInBackground() throws IOException {
- mNfcFingerprints = nfcGetFingerprints();
- mNfcUserId = nfcGetUserId();
- mNfcAid = nfcGetAid();
+ protected void doSecurityTokenInBackground() throws IOException {
+ mSecurityTokenFingerprints = mSecurityTokenHelper.getFingerprints();
+ mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
+ mSecurityTokenAid = mSecurityTokenHelper.getAid();
}
/**
- * Override to handle result of NFC operations (UI thread)
+ * Override to handle result of SecurityToken operations (UI thread)
*/
- protected void onNfcPostExecute() {
+ protected void onSecurityTokenPostExecute() {
- final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
+ final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mSecurityTokenFingerprints);
try {
CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(
@@ -124,15 +110,15 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
Intent intent = new Intent(this, ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mNfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mNfcFingerprints);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
} catch (PgpKeyNotFoundException e) {
Intent intent = new Intent(this, CreateKeyActivity.class);
- intent.putExtra(CreateKeyActivity.EXTRA_NFC_AID, mNfcAid);
- intent.putExtra(CreateKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
- intent.putExtra(CreateKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
+ intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
+ intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
}
}
@@ -140,32 +126,49 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
/**
* Override to use something different than Notify (UI thread)
*/
- protected void onNfcError(String error) {
+ protected void onSecurityTokenError(String error) {
Notify.create(this, error, Style.WARN).show();
}
/**
* Override to do something when PIN is wrong, e.g., clear passphrases (UI thread)
*/
- protected void onNfcPinError(String error) {
- onNfcError(error);
+ protected void onSecurityTokenPinError(String error) {
+ onSecurityTokenError(error);
}
public void tagDiscovered(final Tag tag) {
// Actual NFC operations are executed in doInBackground to not block the UI thread
- if(!mTagHandlingEnabled)
+ if (!mTagHandlingEnabled)
+ return;
+
+ securityTokenDiscovered(new NfcTransport(tag));
+ }
+
+ public void usbDeviceDiscovered(final UsbDevice usbDevice) {
+ // Actual USB operations are executed in doInBackground to not block the UI thread
+ if (!mTagHandlingEnabled)
+ return;
+
+ UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ securityTokenDiscovered(new UsbTransport(usbDevice, usbManager));
+ }
+
+ public void securityTokenDiscovered(final Transport transport) {
+ // Actual Security Token operations are executed in doInBackground to not block the UI thread
+ if (!mTagHandlingEnabled)
return;
new AsyncTask<Void, Void, IOException>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
- onNfcPreExecute();
+ onSecurityTokenPreExecute();
}
@Override
protected IOException doInBackground(Void... params) {
try {
- handleTagDiscovered(tag);
+ handleSecurityToken(transport);
} catch (IOException e) {
return e;
}
@@ -178,11 +181,11 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
super.onPostExecute(exception);
if (exception != null) {
- handleNfcError(exception);
+ handleSecurityTokenError(exception);
return;
}
- onNfcPostExecute();
+ onSecurityTokenPostExecute();
}
}.execute();
}
@@ -200,6 +203,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
super.onCreate(savedInstanceState);
mTagDispatcher = TagDispatcher.get(this, this, false, false, true, false);
+ mUsbDispatcher = new UsbConnectionDispatcher(this, this);
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
@@ -233,15 +237,15 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
mTagDispatcher.interceptIntent(intent);
}
- private void handleNfcError(IOException e) {
+ private void handleSecurityTokenError(IOException e) {
if (e instanceof TagLostException) {
- onNfcError(getString(R.string.security_token_error_tag_lost));
+ onSecurityTokenError(getString(R.string.security_token_error_tag_lost));
return;
}
if (e instanceof IsoDepNotSupportedException) {
- onNfcError(getString(R.string.security_token_error_iso_dep_not_supported));
+ onSecurityTokenError(getString(R.string.security_token_error_iso_dep_not_supported));
return;
}
@@ -256,7 +260,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
if ((status & (short) 0xFFF0) == 0x63C0) {
int tries = status & 0x000F;
// hook to do something different when PIN is wrong
- onNfcPinError(getResources().getQuantityString(R.plurals.security_token_error_pin, tries, tries));
+ onSecurityTokenPinError(getResources().getQuantityString(R.plurals.security_token_error_pin, tries, tries));
return;
}
@@ -265,61 +269,61 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
// These errors should not occur in everyday use; if they are returned, it means we
// made a mistake sending data to the token, or the token is misbehaving.
case 0x6A80: {
- onNfcError(getString(R.string.security_token_error_bad_data));
+ onSecurityTokenError(getString(R.string.security_token_error_bad_data));
break;
}
case 0x6883: {
- onNfcError(getString(R.string.security_token_error_chaining_error));
+ onSecurityTokenError(getString(R.string.security_token_error_chaining_error));
break;
}
case 0x6B00: {
- onNfcError(getString(R.string.security_token_error_header, "P1/P2"));
+ onSecurityTokenError(getString(R.string.security_token_error_header, "P1/P2"));
break;
}
case 0x6D00: {
- onNfcError(getString(R.string.security_token_error_header, "INS"));
+ onSecurityTokenError(getString(R.string.security_token_error_header, "INS"));
break;
}
case 0x6E00: {
- onNfcError(getString(R.string.security_token_error_header, "CLA"));
+ onSecurityTokenError(getString(R.string.security_token_error_header, "CLA"));
break;
}
// These error conditions are more likely to be experienced by an end user.
case 0x6285: {
- onNfcError(getString(R.string.security_token_error_terminated));
+ onSecurityTokenError(getString(R.string.security_token_error_terminated));
break;
}
case 0x6700: {
- onNfcPinError(getString(R.string.security_token_error_wrong_length));
+ onSecurityTokenPinError(getString(R.string.security_token_error_wrong_length));
break;
}
case 0x6982: {
- onNfcError(getString(R.string.security_token_error_security_not_satisfied));
+ onSecurityTokenError(getString(R.string.security_token_error_security_not_satisfied));
break;
}
case 0x6983: {
- onNfcError(getString(R.string.security_token_error_authentication_blocked));
+ onSecurityTokenError(getString(R.string.security_token_error_authentication_blocked));
break;
}
case 0x6985: {
- onNfcError(getString(R.string.security_token_error_conditions_not_satisfied));
+ onSecurityTokenError(getString(R.string.security_token_error_conditions_not_satisfied));
break;
}
// 6A88 is "Not Found" in the spec, but Yubikey also returns 6A83 for this in some cases.
case 0x6A88:
case 0x6A83: {
- onNfcError(getString(R.string.security_token_error_data_not_found));
+ onSecurityTokenError(getString(R.string.security_token_error_data_not_found));
break;
}
// 6F00 is a JavaCard proprietary status code, SW_UNKNOWN, and usually represents an
// unhandled exception on the security token.
case 0x6F00: {
- onNfcError(getString(R.string.security_token_error_unknown));
+ onSecurityTokenError(getString(R.string.security_token_error_unknown));
break;
}
// 6A82 app not installed on security token!
case 0x6A82: {
- if (isFidesmoDevice()) {
+ if (mSecurityTokenHelper.isFidesmoToken()) {
// Check if the Fidesmo app is installed
if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) {
promptFidesmoPgpInstall();
@@ -327,12 +331,12 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
promptFidesmoAppInstall();
}
} else { // Other (possibly) compatible hardware
- onNfcError(getString(R.string.security_token_error_pgp_app_not_installed));
+ onSecurityTokenError(getString(R.string.security_token_error_pgp_app_not_installed));
}
break;
}
default: {
- onNfcError(getString(R.string.security_token_error, e.getMessage()));
+ onSecurityTokenError(getString(R.string.security_token_error, e.getMessage()));
break;
}
}
@@ -366,7 +370,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this,
requiredInput.getMasterKeyId(), requiredInput.getSubKeyId());
if (passphrase != null) {
- mPin = passphrase;
+ mSecurityTokenHelper.setPin(passphrase);
return;
}
@@ -374,7 +378,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT,
RequiredInputParcel.createRequiredPassphrase(requiredInput));
startActivityForResult(intent, REQUEST_CODE_PIN);
- } catch (KeyNotFoundException e) {
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
throw new AssertionError(
"tried to find passphrase for non-existing key. this is a programming error!");
}
@@ -391,7 +395,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
return;
}
CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
- mPin = input.getPassphrase();
+ mSecurityTokenHelper.setPin(input.getPassphrase());
break;
}
default:
@@ -399,591 +403,22 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
}
}
- /** Handle NFC communication and return a result.
- *
- * This method is called by onNewIntent above upon discovery of an NFC tag.
- * It handles initialization and login to the application, subsequently
- * calls either nfcCalculateSignature() or nfcDecryptSessionKey(), then
- * finishes the activity with an appropriate result.
- *
- * On general communication, see also
- * http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx
- *
- * References to pages are generally related to the OpenPGP Application
- * on ISO SmartCard Systems specification.
- *
- */
- protected void handleTagDiscovered(Tag tag) throws IOException {
-
- // Connect to the detected tag, setting a couple of settings
- mIsoCard = AndroidCard.get(tag);
- if (mIsoCard == null) {
- throw new IsoDepNotSupportedException("Tag does not support ISO-DEP (ISO 14443-4)");
+ protected void handleSecurityToken(Transport transport) throws IOException {
+ // Don't reconnect if device was already connected
+ if (!(mSecurityTokenHelper.isPersistentConnectionAllowed()
+ && mSecurityTokenHelper.isConnected()
+ && mSecurityTokenHelper.getTransport().equals(transport))) {
+ mSecurityTokenHelper.setTransport(transport);
+ mSecurityTokenHelper.connectToDevice();
}
- mIsoCard.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation
- mIsoCard.connect();
-
- // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time.
- // See specification, page 51
- String accepted = "9000";
-
- // Command APDU (page 51) for SELECT FILE command (page 29)
- String opening =
- "00" // CLA
- + "A4" // INS
- + "04" // P1
- + "00" // P2
- + "06" // Lc (number of bytes)
- + "D27600012401" // Data (6 bytes)
- + "00"; // Le
- String response = nfcCommunicate(opening); // activate connection
- if ( ! response.endsWith(accepted) ) {
- throw new CardException("Initialization failed!", parseCardStatus(response));
- }
-
- byte[] pwStatusBytes = nfcGetPwStatusBytes();
- mPw1ValidForMultipleSignatures = (pwStatusBytes[0] == 1);
- mPw1ValidatedForSignature = false;
- mPw1ValidatedForDecrypt = false;
- mPw3Validated = false;
-
- doNfcInBackground();
-
+ doSecurityTokenInBackground();
}
- public boolean isNfcConnected() {
- return mIsoCard.isConnected();
+ public boolean isSecurityTokenConnected() {
+ return mSecurityTokenHelper.isConnected();
}
- /** Return the key id from application specific data stored on tag, or null
- * if it doesn't exist.
- *
- * @param idx Index of the key to return the fingerprint from.
- * @return The long key id of the requested key, or null if not found.
- */
- public Long nfcGetKeyId(int idx) throws IOException {
- byte[] fp = nfcGetMasterKeyFingerprint(idx);
- if (fp == null) {
- return null;
- }
- ByteBuffer buf = ByteBuffer.wrap(fp);
- // skip first 12 bytes of the fingerprint
- buf.position(12);
- // the last eight bytes are the key id (big endian, which is default order in ByteBuffer)
- return buf.getLong();
- }
-
- /** Return fingerprints of all keys from application specific data stored
- * on tag, or null if data not available.
- *
- * @return The fingerprints of all subkeys in a contiguous byte array.
- */
- public byte[] nfcGetFingerprints() throws IOException {
- String data = "00CA006E00";
- byte[] buf = mIsoCard.transceive(Hex.decode(data));
-
- Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true);
- Log.d(Constants.TAG, "nfcGetFingerprints() Iso7816TLV tlv data:\n" + tlv.prettyPrint());
-
- Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5);
- if (fptlv == null) {
- return null;
- }
-
- return fptlv.mV;
- }
-
- /** Return the PW Status Bytes from the token. This is a simple DO; no TLV decoding needed.
- *
- * @return Seven bytes in fixed format, plus 0x9000 status word at the end.
- */
- public byte[] nfcGetPwStatusBytes() throws IOException {
- String data = "00CA00C400";
- return mIsoCard.transceive(Hex.decode(data));
- }
-
- /** Return the fingerprint from application specific data stored on tag, or
- * null if it doesn't exist.
- *
- * @param idx Index of the key to return the fingerprint from.
- * @return The fingerprint of the requested key, or null if not found.
- */
- public byte[] nfcGetMasterKeyFingerprint(int idx) throws IOException {
- byte[] data = nfcGetFingerprints();
- if (data == null) {
- return null;
- }
-
- // return the master key fingerprint
- ByteBuffer fpbuf = ByteBuffer.wrap(data);
- byte[] fp = new byte[20];
- fpbuf.position(idx * 20);
- fpbuf.get(fp, 0, 20);
-
- return fp;
- }
-
- public byte[] nfcGetAid() throws IOException {
- String info = "00CA004F00";
- return mIsoCard.transceive(Hex.decode(info));
- }
-
- public String nfcGetUserId() throws IOException {
- String info = "00CA006500";
- return nfcGetHolderName(nfcCommunicate(info));
- }
-
- /**
- * Calls to calculate the signature and returns the MPI value
- *
- * @param hash the hash for signing
- * @return a big integer representing the MPI for the given hash
- */
- public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException {
- if (!mPw1ValidatedForSignature) {
- nfcVerifyPIN(0x81); // (Verify PW1 with mode 81 for signing)
- }
-
- // dsi, including Lc
- String dsi;
-
- Log.i(Constants.TAG, "Hash: " + hashAlgo);
- switch (hashAlgo) {
- case HashAlgorithmTags.SHA1:
- if (hash.length != 20) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 10!");
- }
- dsi = "23" // Lc
- + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes
- + "3009" // Tag/Length of Sequence, the 0x09 are the following header bytes
- + "0605" + "2B0E03021A" // OID of SHA1
- + "0500" // TLV coding of ZERO
- + "0414" + getHex(hash); // 0x14 are 20 hash bytes
- break;
- case HashAlgorithmTags.RIPEMD160:
- if (hash.length != 20) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 20!");
- }
- dsi = "233021300906052B2403020105000414" + getHex(hash);
- break;
- case HashAlgorithmTags.SHA224:
- if (hash.length != 28) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 28!");
- }
- dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash);
- break;
- case HashAlgorithmTags.SHA256:
- if (hash.length != 32) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 32!");
- }
- dsi = "333031300D060960864801650304020105000420" + getHex(hash);
- break;
- case HashAlgorithmTags.SHA384:
- if (hash.length != 48) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 48!");
- }
- dsi = "433041300D060960864801650304020205000430" + getHex(hash);
- break;
- case HashAlgorithmTags.SHA512:
- if (hash.length != 64) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 64!");
- }
- dsi = "533051300D060960864801650304020305000440" + getHex(hash);
- break;
- default:
- throw new IOException("Not supported hash algo!");
- }
-
- // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37)
- String apdu =
- "002A9E9A" // CLA, INS, P1, P2
- + dsi // digital signature input
- + "00"; // Le
-
- String response = nfcCommunicate(apdu);
-
- // split up response into signature and status
- String status = response.substring(response.length()-4);
- String signature = response.substring(0, response.length() - 4);
-
- // while we are getting 0x61 status codes, retrieve more data
- while (status.substring(0, 2).equals("61")) {
- Log.d(Constants.TAG, "requesting more data, status " + status);
- // Send GET RESPONSE command
- response = nfcCommunicate("00C00000" + status.substring(2));
- status = response.substring(response.length()-4);
- signature += response.substring(0, response.length()-4);
- }
-
- Log.d(Constants.TAG, "final response:" + status);
-
- if (!mPw1ValidForMultipleSignatures) {
- mPw1ValidatedForSignature = false;
- }
-
- if ( ! "9000".equals(status)) {
- throw new CardException("Bad NFC response code: " + status, parseCardStatus(response));
- }
-
- // Make sure the signature we received is actually the expected number of bytes long!
- if (signature.length() != 256 && signature.length() != 512) {
- throw new IOException("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2);
- }
-
- return Hex.decode(signature);
- }
-
- /**
- * Calls to calculate the signature and returns the MPI value
- *
- * @param encryptedSessionKey the encoded session key
- * @return the decoded session key
- */
- public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException {
- if (!mPw1ValidatedForDecrypt) {
- nfcVerifyPIN(0x82); // (Verify PW1 with mode 82 for decryption)
- }
-
- String firstApdu = "102a8086fe";
- String secondApdu = "002a808603";
- String le = "00";
-
- byte[] one = new byte[254];
- // leave out first byte:
- System.arraycopy(encryptedSessionKey, 1, one, 0, one.length);
-
- byte[] two = new byte[encryptedSessionKey.length - 1 - one.length];
- for (int i = 0; i < two.length; i++) {
- two[i] = encryptedSessionKey[i + one.length + 1];
- }
-
- String first = nfcCommunicate(firstApdu + getHex(one));
- String second = nfcCommunicate(secondApdu + getHex(two) + le);
-
- String decryptedSessionKey = nfcGetDataField(second);
-
- return Hex.decode(decryptedSessionKey);
- }
-
- /** Verifies the user's PW1 or PW3 with the appropriate mode.
- *
- * @param mode For PW1, this is 0x81 for signing, 0x82 for everything else.
- * For PW3 (Admin PIN), mode is 0x83.
- */
- public void nfcVerifyPIN(int mode) throws IOException {
- if (mPin != null || mode == 0x83) {
-
- byte[] pin;
- if (mode == 0x83) {
- pin = mAdminPin.toStringUnsafe().getBytes();
- } else {
- pin = mPin.toStringUnsafe().getBytes();
- }
-
- // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time.
- // See specification, page 51
- String accepted = "9000";
- String response = tryPin(mode, pin); // login
- if (!response.equals(accepted)) {
- throw new CardException("Bad PIN!", parseCardStatus(response));
- }
-
- if (mode == 0x81) {
- mPw1ValidatedForSignature = true;
- } else if (mode == 0x82) {
- mPw1ValidatedForDecrypt = true;
- } else if (mode == 0x83) {
- mPw3Validated = true;
- }
- }
- }
-
- public void nfcResetCard() throws IOException {
- String accepted = "9000";
-
- // try wrong PIN 4 times until counter goes to C0
- byte[] pin = "XXXXXX".getBytes();
- for (int i = 0; i <= 4; i++) {
- String response = tryPin(0x81, pin);
- if (response.equals(accepted)) { // Should NOT accept!
- throw new CardException("Should never happen, XXXXXX has been accepted!", parseCardStatus(response));
- }
- }
-
- // try wrong Admin PIN 4 times until counter goes to C0
- byte[] adminPin = "XXXXXXXX".getBytes();
- for (int i = 0; i <= 4; i++) {
- String response = tryPin(0x83, adminPin);
- if (response.equals(accepted)) { // Should NOT accept!
- throw new CardException("Should never happen, XXXXXXXX has been accepted", parseCardStatus(response));
- }
- }
-
- // reactivate token!
- String reactivate1 = "00" + "e6" + "00" + "00";
- String reactivate2 = "00" + "44" + "00" + "00";
- String response1 = nfcCommunicate(reactivate1);
- String response2 = nfcCommunicate(reactivate2);
- if (!response1.equals(accepted) || !response2.equals(accepted)) {
- throw new CardException("Reactivating failed!", parseCardStatus(response1));
- }
-
- }
-
- private String tryPin(int mode, byte[] pin) throws IOException {
- // Command APDU for VERIFY command (page 32)
- String login =
- "00" // CLA
- + "20" // INS
- + "00" // P1
- + String.format("%02x", mode) // P2
- + String.format("%02x", pin.length) // Lc
- + Hex.toHexString(pin);
-
- return nfcCommunicate(login);
- }
-
- /** Modifies the user's PW1 or PW3. Before sending, the new PIN will be validated for
- * conformance to the token's requirements for key length.
- *
- * @param pw For PW1, this is 0x81. For PW3 (Admin PIN), mode is 0x83.
- * @param newPin The new PW1 or PW3.
- */
- public void nfcModifyPIN(int pw, byte[] newPin) throws IOException {
- final int MAX_PW1_LENGTH_INDEX = 1;
- final int MAX_PW3_LENGTH_INDEX = 3;
-
- byte[] pwStatusBytes = nfcGetPwStatusBytes();
-
- if (pw == 0x81) {
- if (newPin.length < 6 || newPin.length > pwStatusBytes[MAX_PW1_LENGTH_INDEX]) {
- throw new IOException("Invalid PIN length");
- }
- } else if (pw == 0x83) {
- if (newPin.length < 8 || newPin.length > pwStatusBytes[MAX_PW3_LENGTH_INDEX]) {
- throw new IOException("Invalid PIN length");
- }
- } else {
- throw new IOException("Invalid PW index for modify PIN operation");
- }
-
- byte[] pin;
- if (pw == 0x83) {
- pin = mAdminPin.toStringUnsafe().getBytes();
- } else {
- pin = mPin.toStringUnsafe().getBytes();
- }
-
- // Command APDU for CHANGE REFERENCE DATA command (page 32)
- String changeReferenceDataApdu = "00" // CLA
- + "24" // INS
- + "00" // P1
- + String.format("%02x", pw) // P2
- + String.format("%02x", pin.length + newPin.length) // Lc
- + getHex(pin)
- + getHex(newPin);
- String response = nfcCommunicate(changeReferenceDataApdu); // change PIN
- if (!response.equals("9000")) {
- throw new CardException("Failed to change PIN", parseCardStatus(response));
- }
- }
-
- /**
- * Stores a data object on the token. Automatically validates the proper PIN for the operation.
- * Supported for all data objects < 255 bytes in length. Only the cardholder certificate
- * (0x7F21) can exceed this length.
- *
- * @param dataObject The data object to be stored.
- * @param data The data to store in the object
- */
- public void nfcPutData(int dataObject, byte[] data) throws IOException {
- if (data.length > 254) {
- throw new IOException("Cannot PUT DATA with length > 254");
- }
- if (dataObject == 0x0101 || dataObject == 0x0103) {
- if (!mPw1ValidatedForDecrypt) {
- nfcVerifyPIN(0x82); // (Verify PW1 for non-signing operations)
- }
- } else if (!mPw3Validated) {
- nfcVerifyPIN(0x83); // (Verify PW3)
- }
-
- String putDataApdu = "00" // CLA
- + "DA" // INS
- + String.format("%02x", (dataObject & 0xFF00) >> 8) // P1
- + String.format("%02x", dataObject & 0xFF) // P2
- + String.format("%02x", data.length) // Lc
- + getHex(data);
-
- String response = nfcCommunicate(putDataApdu); // put data
- if (!response.equals("9000")) {
- throw new CardException("Failed to put data.", parseCardStatus(response));
- }
- }
-
- /**
- * Puts a key on the token in the given slot.
- *
- * @param slot The slot on the token where the key should be stored:
- * 0xB6: Signature Key
- * 0xB8: Decipherment Key
- * 0xA4: Authentication Key
- */
- public void nfcPutKey(int slot, CanonicalizedSecretKey secretKey, Passphrase passphrase)
- throws IOException {
- if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) {
- throw new IOException("Invalid key slot");
- }
-
- RSAPrivateCrtKey crtSecretKey;
- try {
- secretKey.unlock(passphrase);
- crtSecretKey = secretKey.getCrtSecretKey();
- } catch (PgpGeneralException e) {
- throw new IOException(e.getMessage());
- }
-
- // Shouldn't happen; the UI should block the user from getting an incompatible key this far.
- if (crtSecretKey.getModulus().bitLength() > 2048) {
- throw new IOException("Key too large to export to Security Token.");
- }
-
- // Should happen only rarely; all GnuPG keys since 2006 use public exponent 65537.
- if (!crtSecretKey.getPublicExponent().equals(new BigInteger("65537"))) {
- throw new IOException("Invalid public exponent for smart Security Token.");
- }
-
- if (!mPw3Validated) {
- nfcVerifyPIN(0x83); // (Verify PW3 with mode 83)
- }
-
- byte[] header= Hex.decode(
- "4D82" + "03A2" // Extended header list 4D82, length of 930 bytes. (page 23)
- + String.format("%02x", slot) + "00" // CRT to indicate targeted key, no length
- + "7F48" + "15" // Private key template 0x7F48, length 21 (decimal, 0x15 hex)
- + "9103" // Public modulus, length 3
- + "928180" // Prime P, length 128
- + "938180" // Prime Q, length 128
- + "948180" // Coefficient (1/q mod p), length 128
- + "958180" // Prime exponent P (d mod (p - 1)), length 128
- + "968180" // Prime exponent Q (d mod (1 - 1)), length 128
- + "97820100" // Modulus, length 256, last item in private key template
- + "5F48" + "820383");// DO 5F48; 899 bytes of concatenated key data will follow
- byte[] dataToSend = new byte[934];
- byte[] currentKeyObject;
- int offset = 0;
-
- System.arraycopy(header, 0, dataToSend, offset, header.length);
- offset += header.length;
- currentKeyObject = crtSecretKey.getPublicExponent().toByteArray();
- System.arraycopy(currentKeyObject, 0, dataToSend, offset, 3);
- offset += 3;
- // NOTE: For a 2048-bit key, these lengths are fixed. However, bigint includes a leading 0
- // in the array to represent sign, so we take care to set the offset to 1 if necessary.
- currentKeyObject = crtSecretKey.getPrimeP().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getPrimeQ().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getCrtCoefficient().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getPrimeExponentP().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getPrimeExponentQ().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getModulus().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 256, dataToSend, offset, 256);
-
- String putKeyCommand = "10DB3FFF";
- String lastPutKeyCommand = "00DB3FFF";
-
- // Now we're ready to communicate with the token.
- offset = 0;
- String response;
- while(offset < dataToSend.length) {
- int dataRemaining = dataToSend.length - offset;
- if (dataRemaining > 254) {
- response = nfcCommunicate(
- putKeyCommand + "FE" + Hex.toHexString(dataToSend, offset, 254)
- );
- offset += 254;
- } else {
- int length = dataToSend.length - offset;
- response = nfcCommunicate(
- lastPutKeyCommand + String.format("%02x", length)
- + Hex.toHexString(dataToSend, offset, length));
- offset += length;
- }
-
- if (!response.endsWith("9000")) {
- throw new CardException("Key export to Security Token failed", parseCardStatus(response));
- }
- }
-
- // Clear array with secret data before we return.
- Arrays.fill(dataToSend, (byte) 0);
- }
-
- /**
- * Parses out the status word from a JavaCard response string.
- *
- * @param response A hex string with the response from the token
- * @return A short indicating the SW1/SW2, or 0 if a status could not be determined.
- */
- short parseCardStatus(String response) {
- if (response.length() < 4) {
- return 0; // invalid input
- }
-
- try {
- return Short.parseShort(response.substring(response.length() - 4), 16);
- } catch (NumberFormatException e) {
- return 0;
- }
- }
-
- public String nfcGetHolderName(String name) {
- try {
- String slength;
- int ilength;
- name = name.substring(6);
- slength = name.substring(0, 2);
- ilength = Integer.parseInt(slength, 16) * 2;
- name = name.substring(2, ilength + 2);
- name = (new String(Hex.decode(name))).replace('<', ' ');
- return name;
- } catch (IndexOutOfBoundsException e) {
- // try-catch for https://github.com/FluffyKaon/OpenPGP-Card
- // Note: This should not happen, but happens with
- // https://github.com/FluffyKaon/OpenPGP-Card, thus return an empty string for now!
-
- Log.e(Constants.TAG, "Couldn't get holder name, returning empty string!", e);
- return "";
- }
- }
-
- private String nfcGetDataField(String output) {
- return output.substring(0, output.length() - 4);
- }
-
- public String nfcCommunicate(String apdu) throws IOException {
- return getHex(mIsoCard.transceive(Hex.decode(apdu)));
- }
-
- public static String getHex(byte[] raw) {
- return new String(Hex.encode(raw));
- }
-
- public class IsoDepNotSupportedException extends IOException {
+ public static class IsoDepNotSupportedException extends IOException {
public IsoDepNotSupportedException(String detailMessage) {
super(detailMessage);
@@ -991,41 +426,12 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
}
- public class CardException extends IOException {
- private short mResponseCode;
-
- public CardException(String detailMessage, short responseCode) {
- super(detailMessage);
- mResponseCode = responseCode;
- }
-
- public short getResponseCode() {
- return mResponseCode;
- }
-
- }
-
- private boolean isFidesmoDevice() {
- if (isNfcConnected()) { // Check if we can still talk to the card
- try {
- // By trying to select any apps that have the Fidesmo AID prefix we can
- // see if it is a Fidesmo device or not
- byte[] mSelectResponse = mIsoCard.transceive(Apdu.select(FIDESMO_APPS_AID_PREFIX));
- // Compare the status returned by our select with the OK status code
- return Apdu.hasStatus(mSelectResponse, Apdu.OK_APDU);
- } catch (IOException e) {
- Log.e(Constants.TAG, "Card communication failed!", e);
- }
- }
- return false;
- }
-
/**
- * Ask user if she wants to install PGP onto her Fidesmo device
- */
+ * Ask user if she wants to install PGP onto her Fidesmo token
+ */
private void promptFidesmoPgpInstall() {
- FidesmoPgpInstallDialog mFidesmoPgpInstallDialog = new FidesmoPgpInstallDialog();
- mFidesmoPgpInstallDialog.show(getSupportFragmentManager(), "mFidesmoPgpInstallDialog");
+ FidesmoPgpInstallDialog fidesmoPgpInstallDialog = new FidesmoPgpInstallDialog();
+ fidesmoPgpInstallDialog.show(getSupportFragmentManager(), "fidesmoPgpInstallDialog");
}
/**
@@ -1033,18 +439,19 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
* to launch the Google Play store.
*/
private void promptFidesmoAppInstall() {
- FidesmoInstallDialog mFidesmoInstallDialog = new FidesmoInstallDialog();
- mFidesmoInstallDialog.show(getSupportFragmentManager(), "mFidesmoInstallDialog");
+ FidesmoInstallDialog fidesmoInstallDialog = new FidesmoInstallDialog();
+ fidesmoInstallDialog.show(getSupportFragmentManager(), "fidesmoInstallDialog");
}
/**
* Use the package manager to detect if an application is installed on the phone
+ *
* @param uri an URI identifying the application's package
* @return 'true' if the app is installed
*/
private boolean isAndroidAppInstalled(String uri) {
PackageManager mPackageManager = getPackageManager();
- boolean mAppInstalled = false;
+ boolean mAppInstalled;
try {
mPackageManager.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
mAppInstalled = true;
@@ -1054,4 +461,28 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
}
return mAppInstalled;
}
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mUsbDispatcher.onStop();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mUsbDispatcher.onStart();
+ }
+
+ public SecurityTokenHelper getSecurityTokenHelper() {
+ return mSecurityTokenHelper;
+ }
+
+ /**
+ * Run Security Token routines if last used token is connected and supports
+ * persistent connections
+ */
+ public void checkDeviceConnection() {
+ mUsbDispatcher.rescanDevices();
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
index 451065d6b..ad15c8f68 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
@@ -130,9 +130,9 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
switch (requiredInput.mType) {
// always use CryptoOperationHelper.startActivityForResult!
- case NFC_MOVE_KEY_TO_CARD:
- case NFC_DECRYPT:
- case NFC_SIGN: {
+ case SECURITY_TOKEN_MOVE_KEY_TO_CARD:
+ case SECURITY_TOKEN_DECRYPT:
+ case SECURITY_TOKEN_SIGN: {
Intent intent = new Intent(activity, SecurityTokenOperationActivity.class);
intent.putExtra(SecurityTokenOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
intent.putExtra(SecurityTokenOperationActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java
index 3d96f3c6d..3dcc2f58b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java
@@ -50,14 +50,13 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
-import com.squareup.okhttp.OkHttpClient;
-import com.squareup.okhttp.Request;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.OkHttpClientFactory;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.TlsHelper;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
@@ -354,19 +353,15 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
Log.d("Converted URL", newKeyserver.toString());
- OkHttpClient client = HkpKeyserver.getClient(newKeyserver.toURL(), proxy);
-
- // don't follow any redirects
- client.setFollowRedirects(false);
- client.setFollowSslRedirects(false);
-
if (onlyTrustedKeyserver
- && !TlsHelper.usePinnedCertificateIfAvailable(client, newKeyserver.toURL())) {
+ && TlsHelper.getPinnedSslSocketFactory(newKeyserver.toURL()) == null) {
Log.w(Constants.TAG, "No pinned certificate for this host in OpenKeychain's assets.");
reason = FailureReason.NO_PINNED_CERTIFICATE;
return reason;
}
+ OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(newKeyserver.toURL(), proxy);
+
client.newCall(new Request.Builder().url(newKeyserver.toURL()).build()).execute();
} catch (TlsHelper.TlsHelperException e) {
reason = FailureReason.CONNECTION_FAILED;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
index 5b75723fb..ce1665382 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
@@ -17,25 +17,26 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import android.annotation.TargetApi;
+import android.annotation.SuppressLint;
import android.app.Dialog;
-import android.os.Build;
+import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AlertDialog;
-import android.text.Editable;
-import android.text.TextWatcher;
+import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.inputmethod.InputMethodManager;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
-import android.widget.EditText;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TableRow;
import android.widget.TextView;
@@ -49,14 +50,18 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.util.Choice;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Calendar;
+import java.util.List;
import java.util.TimeZone;
public class AddSubkeyDialogFragment extends DialogFragment {
public interface OnAlgorithmSelectedListener {
- public void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey);
+ void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey);
+ }
+
+ public enum SupportedKeyType {
+ RSA_2048, RSA_3072, RSA_4096, ECC_P256, ECC_P521
}
private static final String ARG_WILL_BE_MASTER_KEY = "will_be_master_key";
@@ -66,18 +71,12 @@ public class AddSubkeyDialogFragment extends DialogFragment {
private CheckBox mNoExpiryCheckBox;
private TableRow mExpiryRow;
private DatePicker mExpiryDatePicker;
- private Spinner mAlgorithmSpinner;
- private View mKeySizeRow;
- private Spinner mKeySizeSpinner;
- private View mCurveRow;
- private Spinner mCurveSpinner;
- private TextView mCustomKeyTextView;
- private EditText mCustomKeyEditText;
- private TextView mCustomKeyInfoTextView;
- private CheckBox mFlagCertify;
- private CheckBox mFlagSign;
- private CheckBox mFlagEncrypt;
- private CheckBox mFlagAuthenticate;
+ private Spinner mKeyTypeSpinner;
+ private RadioGroup mUsageRadioGroup;
+ private RadioButton mUsageNone;
+ private RadioButton mUsageSign;
+ private RadioButton mUsageEncrypt;
+ private RadioButton mUsageSignAndEncrypt;
private boolean mWillBeMasterKey;
@@ -96,6 +95,8 @@ public class AddSubkeyDialogFragment extends DialogFragment {
return frag;
}
+
+ @NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity context = getActivity();
@@ -106,25 +107,27 @@ public class AddSubkeyDialogFragment extends DialogFragment {
CustomAlertDialogBuilder dialog = new CustomAlertDialogBuilder(context);
+ @SuppressLint("InflateParams")
View view = mInflater.inflate(R.layout.add_subkey_dialog, null);
dialog.setView(view);
- dialog.setTitle(R.string.title_add_subkey);
mNoExpiryCheckBox = (CheckBox) view.findViewById(R.id.add_subkey_no_expiry);
mExpiryRow = (TableRow) view.findViewById(R.id.add_subkey_expiry_row);
mExpiryDatePicker = (DatePicker) view.findViewById(R.id.add_subkey_expiry_date_picker);
- mAlgorithmSpinner = (Spinner) view.findViewById(R.id.add_subkey_algorithm);
- mKeySizeSpinner = (Spinner) view.findViewById(R.id.add_subkey_size);
- mCurveSpinner = (Spinner) view.findViewById(R.id.add_subkey_curve);
- mKeySizeRow = view.findViewById(R.id.add_subkey_row_size);
- mCurveRow = view.findViewById(R.id.add_subkey_row_curve);
- mCustomKeyTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_label);
- mCustomKeyEditText = (EditText) view.findViewById(R.id.add_subkey_custom_key_size_input);
- mCustomKeyInfoTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_info);
- mFlagCertify = (CheckBox) view.findViewById(R.id.add_subkey_flag_certify);
- mFlagSign = (CheckBox) view.findViewById(R.id.add_subkey_flag_sign);
- mFlagEncrypt = (CheckBox) view.findViewById(R.id.add_subkey_flag_encrypt);
- mFlagAuthenticate = (CheckBox) view.findViewById(R.id.add_subkey_flag_authenticate);
+ mKeyTypeSpinner = (Spinner) view.findViewById(R.id.add_subkey_type);
+ mUsageRadioGroup = (RadioGroup) view.findViewById(R.id.add_subkey_usage_group);
+ mUsageNone = (RadioButton) view.findViewById(R.id.add_subkey_usage_none);
+ mUsageSign = (RadioButton) view.findViewById(R.id.add_subkey_usage_sign);
+ mUsageEncrypt = (RadioButton) view.findViewById(R.id.add_subkey_usage_encrypt);
+ mUsageSignAndEncrypt = (RadioButton) view.findViewById(R.id.add_subkey_usage_sign_and_encrypt);
+
+ if(mWillBeMasterKey) {
+ dialog.setTitle(R.string.title_change_master_key);
+ mUsageNone.setVisibility(View.VISIBLE);
+ mUsageNone.setChecked(true);
+ } else {
+ dialog.setTitle(R.string.title_add_subkey);
+ }
mNoExpiryCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
@@ -143,65 +146,24 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mExpiryDatePicker.setMinDate(minDateCal.getTime().getTime());
{
- ArrayList<Choice<Algorithm>> choices = new ArrayList<>();
- choices.add(new Choice<>(Algorithm.DSA, getResources().getString(
- R.string.dsa)));
- if (!mWillBeMasterKey) {
- choices.add(new Choice<>(Algorithm.ELGAMAL, getResources().getString(
- R.string.elgamal)));
- }
- choices.add(new Choice<>(Algorithm.RSA, getResources().getString(
- R.string.rsa)));
- choices.add(new Choice<>(Algorithm.ECDSA, getResources().getString(
- R.string.ecdsa)));
- choices.add(new Choice<>(Algorithm.ECDH, getResources().getString(
- R.string.ecdh)));
- ArrayAdapter<Choice<Algorithm>> adapter = new ArrayAdapter<>(context,
+ ArrayList<Choice<SupportedKeyType>> choices = new ArrayList<>();
+ choices.add(new Choice<>(SupportedKeyType.RSA_2048, getResources().getString(
+ R.string.rsa_2048), getResources().getString(R.string.rsa_2048_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.RSA_3072, getResources().getString(
+ R.string.rsa_3072), getResources().getString(R.string.rsa_3072_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.RSA_4096, getResources().getString(
+ R.string.rsa_4096), getResources().getString(R.string.rsa_4096_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.ECC_P256, getResources().getString(
+ R.string.ecc_p256), getResources().getString(R.string.ecc_p256_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.ECC_P521, getResources().getString(
+ R.string.ecc_p521), getResources().getString(R.string.ecc_p521_description_html)));
+ TwoLineArrayAdapter adapter = new TwoLineArrayAdapter(context,
android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mAlgorithmSpinner.setAdapter(adapter);
- // make RSA the default
+ mKeyTypeSpinner.setAdapter(adapter);
+ // make RSA 3072 the default
for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == Algorithm.RSA) {
- mAlgorithmSpinner.setSelection(i);
- break;
- }
- }
- }
-
- // dynamic ArrayAdapter must be created (instead of ArrayAdapter.getFromResource), because it's content may change
- ArrayAdapter<CharSequence> keySizeAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item,
- new ArrayList<CharSequence>(Arrays.asList(getResources().getStringArray(R.array.rsa_key_size_spinner_values))));
- keySizeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mKeySizeSpinner.setAdapter(keySizeAdapter);
- mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length
-
- {
- ArrayList<Choice<Curve>> choices = new ArrayList<>();
-
- choices.add(new Choice<>(Curve.NIST_P256, getResources().getString(
- R.string.key_curve_nist_p256)));
- choices.add(new Choice<>(Curve.NIST_P384, getResources().getString(
- R.string.key_curve_nist_p384)));
- choices.add(new Choice<>(Curve.NIST_P521, getResources().getString(
- R.string.key_curve_nist_p521)));
-
- /* @see SaveKeyringParcel
- choices.add(new Choice<Curve>(Curve.BRAINPOOL_P256, getResources().getString(
- R.string.key_curve_bp_p256)));
- choices.add(new Choice<Curve>(Curve.BRAINPOOL_P384, getResources().getString(
- R.string.key_curve_bp_p384)));
- choices.add(new Choice<Curve>(Curve.BRAINPOOL_P512, getResources().getString(
- R.string.key_curve_bp_p512)));
- */
-
- ArrayAdapter<Choice<Curve>> adapter = new ArrayAdapter<>(context,
- android.R.layout.simple_spinner_item, choices);
- mCurveSpinner.setAdapter(adapter);
- // make NIST P-256 the default
- for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == Curve.NIST_P256) {
- mCurveSpinner.setSelection(i);
+ if (choices.get(i).getId() == SupportedKeyType.RSA_3072) {
+ mKeyTypeSpinner.setSelection(i);
break;
}
}
@@ -215,45 +177,35 @@ public class AddSubkeyDialogFragment extends DialogFragment {
final AlertDialog alertDialog = dialog.show();
- mCustomKeyEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
+ mKeyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
- public void afterTextChanged(Editable s) {
- setOkButtonAvailability(alertDialog);
- }
- });
-
- mKeySizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- setCustomKeyVisibility();
- setOkButtonAvailability(alertDialog);
- }
+ // noinspection unchecked
+ SupportedKeyType keyType = ((Choice<SupportedKeyType>) parent.getSelectedItem()).getId();
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
+ // RadioGroup.getCheckedRadioButtonId() gives the wrong RadioButton checked
+ // when programmatically unchecking children radio buttons. Clearing all is the only option.
+ mUsageRadioGroup.clearCheck();
- mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- updateUiForAlgorithm(((Choice<Algorithm>) parent.getSelectedItem()).getId());
+ if(mWillBeMasterKey) {
+ mUsageNone.setChecked(true);
+ }
- setCustomKeyVisibility();
- setOkButtonAvailability(alertDialog);
+ if (keyType == SupportedKeyType.ECC_P521 || keyType == SupportedKeyType.ECC_P256) {
+ mUsageSignAndEncrypt.setEnabled(false);
+ if (mWillBeMasterKey) {
+ mUsageEncrypt.setEnabled(false);
+ }
+ } else {
+ // need to enable if previously disabled for ECC masterkey
+ mUsageEncrypt.setEnabled(true);
+ mUsageSignAndEncrypt.setEnabled(true);
+ }
}
@Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
+ public void onNothingSelected(AdapterView<?> parent) {}
});
return alertDialog;
@@ -269,36 +221,74 @@ public class AddSubkeyDialogFragment extends DialogFragment {
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (!mFlagCertify.isChecked() && !mFlagSign.isChecked()
- && !mFlagEncrypt.isChecked() && !mFlagAuthenticate.isChecked()) {
- Toast.makeText(getActivity(), R.string.edit_key_select_flag, Toast.LENGTH_LONG).show();
+ if (mUsageRadioGroup.getCheckedRadioButtonId() == -1) {
+ Toast.makeText(getActivity(), R.string.edit_key_select_usage, Toast.LENGTH_LONG).show();
return;
}
- Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId();
+ // noinspection unchecked
+ SupportedKeyType keyType = ((Choice<SupportedKeyType>) mKeyTypeSpinner.getSelectedItem()).getId();
Curve curve = null;
Integer keySize = null;
- // For EC keys, add a curve
- if (algorithm == Algorithm.ECDH || algorithm == Algorithm.ECDSA) {
- curve = ((Choice<Curve>) mCurveSpinner.getSelectedItem()).getId();
- // Otherwise, get a keysize
- } else {
- keySize = getProperKeyLength(algorithm, getSelectedKeyLength());
+ Algorithm algorithm = null;
+
+ // set keysize & curve, for RSA & ECC respectively
+ switch (keyType) {
+ case RSA_2048: {
+ keySize = 2048;
+ break;
+ }
+ case RSA_3072: {
+ keySize = 3072;
+ break;
+ }
+ case RSA_4096: {
+ keySize = 4096;
+ break;
+ }
+ case ECC_P256: {
+ curve = Curve.NIST_P256;
+ break;
+ }
+ case ECC_P521: {
+ curve = Curve.NIST_P521;
+ break;
+ }
}
+ // set algorithm
+ switch (keyType) {
+ case RSA_2048:
+ case RSA_3072:
+ case RSA_4096: {
+ algorithm = Algorithm.RSA;
+ break;
+ }
+
+ case ECC_P256:
+ case ECC_P521: {
+ if(mUsageEncrypt.isChecked()) {
+ algorithm = Algorithm.ECDH;
+ } else {
+ algorithm = Algorithm.ECDSA;
+ }
+ break;
+ }
+ }
+
+ // set flags
int flags = 0;
- if (mFlagCertify.isChecked()) {
+ if (mWillBeMasterKey) {
flags |= KeyFlags.CERTIFY_OTHER;
}
- if (mFlagSign.isChecked()) {
+ if (mUsageSign.isChecked()) {
flags |= KeyFlags.SIGN_DATA;
- }
- if (mFlagEncrypt.isChecked()) {
+ } else if (mUsageEncrypt.isChecked()) {
flags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
+ } else if (mUsageSignAndEncrypt.isChecked()) {
+ flags |= KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
}
- if (mFlagAuthenticate.isChecked()) {
- flags |= KeyFlags.AUTHENTICATION;
- }
+
long expiry;
if (mNoExpiryCheckBox.isChecked()) {
@@ -332,206 +322,29 @@ public class AddSubkeyDialogFragment extends DialogFragment {
}
}
- private int getSelectedKeyLength() {
- final String selectedItemString = (String) mKeySizeSpinner.getSelectedItem();
- final String customLengthString = getResources().getString(R.string.key_size_custom);
- final boolean customSelected = customLengthString.equals(selectedItemString);
- String keyLengthString = customSelected ? mCustomKeyEditText.getText().toString() : selectedItemString;
- int keySize;
- try {
- keySize = Integer.parseInt(keyLengthString);
- } catch (NumberFormatException e) {
- keySize = 0;
- }
- return keySize;
- }
-
- /**
- * <h3>RSA</h3>
- * <p>for RSA algorithm, key length must be greater than 2048. Possibility to generate keys bigger
- * than 8192 bits is currently disabled, because it's almost impossible to generate them on a mobile device (check
- * <a href="http://www.javamex.com/tutorials/cryptography/rsa_key_length.shtml">RSA key length plot</a> and
- * <a href="http://www.keylength.com/">Cryptographic Key Length Recommendation</a>). Also, key length must be a
- * multiplicity of 8.</p>
- * <h3>ElGamal</h3>
- * <p>For ElGamal algorithm, supported key lengths are 2048, 3072, 4096 or 8192 bits.</p>
- * <h3>DSA</h3>
- * <p>For DSA algorithm key length must be between 2048 and 3072. Also, it must me dividable by 64.</p>
- *
- * @return correct key length, according to BouncyCastle specification. Returns <code>-1</code>, if key length is
- * inappropriate.
- */
- private int getProperKeyLength(Algorithm algorithm, int currentKeyLength) {
- final int[] elGamalSupportedLengths = {2048, 3072, 4096, 8192};
- int properKeyLength = -1;
- switch (algorithm) {
- case RSA: {
- if (currentKeyLength >= 2048 && currentKeyLength <= 16384) {
- properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8);
- }
- break;
- }
- case ELGAMAL: {
- int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length];
- for (int i = 0; i < elGamalSupportedLengths.length; i++) {
- elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength);
- }
- int minimalValue = Integer.MAX_VALUE;
- int minimalIndex = -1;
- for (int i = 0; i < elGammalKeyDiff.length; i++) {
- if (elGammalKeyDiff[i] <= minimalValue) {
- minimalValue = elGammalKeyDiff[i];
- minimalIndex = i;
- }
- }
- properKeyLength = elGamalSupportedLengths[minimalIndex];
- break;
- }
- case DSA: {
- // Bouncy Castle supports 4096 maximum
- if (currentKeyLength >= 2048 && currentKeyLength <= 4096) {
- properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64);
- }
- break;
- }
+ private class TwoLineArrayAdapter extends ArrayAdapter<Choice<SupportedKeyType>> {
+ public TwoLineArrayAdapter(Context context, int resource, List<Choice<SupportedKeyType>> objects) {
+ super(context, resource, objects);
}
- return properKeyLength;
- }
- private void setOkButtonAvailability(AlertDialog alertDialog) {
- Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId();
- boolean enabled = algorithm == Algorithm.ECDSA || algorithm == Algorithm.ECDH
- || getProperKeyLength(algorithm, getSelectedKeyLength()) > 0;
- alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled);
- }
- private void setCustomKeyVisibility() {
- final String selectedItemString = (String) mKeySizeSpinner.getSelectedItem();
- final String customLengthString = getResources().getString(R.string.key_size_custom);
- final boolean customSelected = customLengthString.equals(selectedItemString);
- final int visibility = customSelected ? View.VISIBLE : View.GONE;
-
- mCustomKeyEditText.setVisibility(visibility);
- mCustomKeyTextView.setVisibility(visibility);
- mCustomKeyInfoTextView.setVisibility(visibility);
-
- // hide keyboard after setting visibility to gone
- if (visibility == View.GONE) {
- InputMethodManager imm = (InputMethodManager)
- getActivity().getSystemService(FragmentActivity.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mCustomKeyEditText.getWindowToken(), 0);
- }
- }
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ // inflate view if not given one
+ if (convertView == null) {
+ convertView = getActivity().getLayoutInflater()
+ .inflate(R.layout.two_line_spinner_dropdown_item, parent, false);
+ }
- private void updateUiForAlgorithm(Algorithm algorithm) {
- final ArrayAdapter<CharSequence> keySizeAdapter = (ArrayAdapter<CharSequence>) mKeySizeSpinner.getAdapter();
- keySizeAdapter.clear();
- switch (algorithm) {
- case RSA: {
- replaceArrayAdapterContent(keySizeAdapter, R.array.rsa_key_size_spinner_values);
- mKeySizeSpinner.setSelection(1);
- mKeySizeRow.setVisibility(View.VISIBLE);
- mCurveRow.setVisibility(View.GONE);
- mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_rsa));
- // allowed flags:
- mFlagSign.setEnabled(true);
- mFlagEncrypt.setEnabled(true);
- mFlagAuthenticate.setEnabled(true);
-
- if (mWillBeMasterKey) {
- mFlagCertify.setEnabled(true);
-
- mFlagCertify.setChecked(true);
- mFlagSign.setChecked(false);
- mFlagEncrypt.setChecked(false);
- } else {
- mFlagCertify.setEnabled(false);
+ Choice c = this.getItem(position);
- mFlagCertify.setChecked(false);
- mFlagSign.setChecked(true);
- mFlagEncrypt.setChecked(true);
- }
- mFlagAuthenticate.setChecked(false);
- break;
- }
- case ELGAMAL: {
- replaceArrayAdapterContent(keySizeAdapter, R.array.elgamal_key_size_spinner_values);
- mKeySizeSpinner.setSelection(3);
- mKeySizeRow.setVisibility(View.VISIBLE);
- mCurveRow.setVisibility(View.GONE);
- mCustomKeyInfoTextView.setText(""); // ElGamal does not support custom key length
- // allowed flags:
- mFlagCertify.setChecked(false);
- mFlagCertify.setEnabled(false);
- mFlagSign.setChecked(false);
- mFlagSign.setEnabled(false);
- mFlagEncrypt.setChecked(true);
- mFlagEncrypt.setEnabled(true);
- mFlagAuthenticate.setChecked(false);
- mFlagAuthenticate.setEnabled(false);
- break;
- }
- case DSA: {
- replaceArrayAdapterContent(keySizeAdapter, R.array.dsa_key_size_spinner_values);
- mKeySizeSpinner.setSelection(2);
- mKeySizeRow.setVisibility(View.VISIBLE);
- mCurveRow.setVisibility(View.GONE);
- mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_dsa));
- // allowed flags:
- mFlagCertify.setChecked(false);
- mFlagCertify.setEnabled(false);
- mFlagSign.setChecked(true);
- mFlagSign.setEnabled(true);
- mFlagEncrypt.setChecked(false);
- mFlagEncrypt.setEnabled(false);
- mFlagAuthenticate.setChecked(false);
- mFlagAuthenticate.setEnabled(false);
- break;
- }
- case ECDSA: {
- mKeySizeRow.setVisibility(View.GONE);
- mCurveRow.setVisibility(View.VISIBLE);
- mCustomKeyInfoTextView.setText("");
- // allowed flags:
- mFlagCertify.setEnabled(mWillBeMasterKey);
- mFlagCertify.setChecked(mWillBeMasterKey);
- mFlagSign.setEnabled(true);
- mFlagSign.setChecked(!mWillBeMasterKey);
- mFlagEncrypt.setEnabled(false);
- mFlagEncrypt.setChecked(false);
- mFlagAuthenticate.setEnabled(true);
- mFlagAuthenticate.setChecked(false);
- break;
- }
- case ECDH: {
- mKeySizeRow.setVisibility(View.GONE);
- mCurveRow.setVisibility(View.VISIBLE);
- mCustomKeyInfoTextView.setText("");
- // allowed flags:
- mFlagCertify.setChecked(false);
- mFlagCertify.setEnabled(false);
- mFlagSign.setChecked(false);
- mFlagSign.setEnabled(false);
- mFlagEncrypt.setChecked(true);
- mFlagEncrypt.setEnabled(true);
- mFlagAuthenticate.setChecked(false);
- mFlagAuthenticate.setEnabled(false);
- break;
- }
- }
- keySizeAdapter.notifyDataSetChanged();
+ TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
+ TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
- }
+ text1.setText(c.getName());
+ text2.setText(Html.fromHtml(c.getDescription()));
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- private void replaceArrayAdapterContent(ArrayAdapter<CharSequence> arrayAdapter, int stringArrayResourceId) {
- final String[] spinnerValuesStringArray = getResources().getStringArray(stringArrayResourceId);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- arrayAdapter.addAll(spinnerValuesStringArray);
- } else {
- for (final String value : spinnerValuesStringArray) {
- arrayAdapter.add(value);
- }
+ return convertView;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
index b51648740..0c0877bae 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
@@ -35,7 +35,11 @@ public class EditSubkeyDialogFragment extends DialogFragment {
public static final int MESSAGE_CHANGE_EXPIRY = 1;
public static final int MESSAGE_REVOKE = 2;
public static final int MESSAGE_STRIP = 3;
- public static final int MESSAGE_MOVE_KEY_TO_CARD = 4;
+ public static final int MESSAGE_MOVE_KEY_TO_SECURITY_TOKEN = 4;
+ public static final int SUBKEY_MENU_CHANGE_EXPIRY = 0;
+ public static final int SUBKEY_MENU_REVOKE_SUBKEY = 1;
+ public static final int SUBKEY_MENU_STRIP_SUBKEY = 2;
+ public static final int SUBKEY_MENU_MOVE_TO_SECURITY_TOKEN = 3;
private Messenger mMessenger;
@@ -68,17 +72,17 @@ public class EditSubkeyDialogFragment extends DialogFragment {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
- case 0:
+ case SUBKEY_MENU_CHANGE_EXPIRY:
sendMessageToHandler(MESSAGE_CHANGE_EXPIRY, null);
break;
- case 1:
+ case SUBKEY_MENU_REVOKE_SUBKEY:
sendMessageToHandler(MESSAGE_REVOKE, null);
break;
- case 2:
- sendMessageToHandler(MESSAGE_STRIP, null);
+ case SUBKEY_MENU_STRIP_SUBKEY:
+ showAlertDialog();
break;
- case 3:
- sendMessageToHandler(MESSAGE_MOVE_KEY_TO_CARD, null);
+ case SUBKEY_MENU_MOVE_TO_SECURITY_TOKEN:
+ sendMessageToHandler(MESSAGE_MOVE_KEY_TO_SECURITY_TOKEN, null);
break;
default:
break;
@@ -95,6 +99,25 @@ public class EditSubkeyDialogFragment extends DialogFragment {
return builder.show();
}
+ private void showAlertDialog() {
+ CustomAlertDialogBuilder stripAlertDialog = new CustomAlertDialogBuilder(getActivity());
+ stripAlertDialog.setTitle(R.string.title_alert_strip).
+ setMessage(R.string.alert_strip).setCancelable(true);
+ stripAlertDialog.setPositiveButton(R.string.strip, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ sendMessageToHandler(MESSAGE_STRIP, null);
+ }
+ });
+ stripAlertDialog.setNegativeButton(R.string.btn_do_not_save, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ dismiss();
+ }
+ });
+ stripAlertDialog.show();
+ }
+
/**
* Send message back to handler which is initialized in a activity
*
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java
index c25f775b0..334a7361b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java
@@ -119,7 +119,7 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment {
private static Boolean checkHandle(String handle) {
try {
HttpURLConnection nection =
- (HttpURLConnection) new URL("https://twitter.com/" + handle).openConnection();
+ (HttpURLConnection) new URL("https://twitter.com/" + handle).getUrlResponse();
nection.setRequestMethod("HEAD");
nection.setRequestProperty("User-Agent", "OpenKeychain");
return nection.getResponseCode() == 200;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
index 5fcc3d58b..4dc0ebaa0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
@@ -64,8 +64,8 @@ public class KeyFormattingUtils {
String algorithmStr;
switch (algorithm) {
- case PublicKeyAlgorithmTags.RSA_ENCRYPT:
case PublicKeyAlgorithmTags.RSA_GENERAL:
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
case PublicKeyAlgorithmTags.RSA_SIGN: {
algorithmStr = "RSA";
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java
new file mode 100644
index 000000000..7919a0918
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 Alex Fong Jie Wen <alexfongg@gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.util.spinner;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Spinner;
+
+/**
+ * Custom spinner which uses a hack to
+ * always set focus on first item in list
+ *
+ */
+public class FocusFirstItemSpinner extends Spinner {
+ /**
+ * Spinner is originally designed to set focus on the currently selected item.
+ * When Spinner is selected to show dropdown, 'performClick()' is called internally.
+ * 'getSelectedItemPosition()' is then called to obtain the item to focus on.
+ * We use a toggle to have 'getSelectedItemPosition()' return the 0th index
+ * for this particular case.
+ */
+
+ private boolean mToggleFlag = true;
+
+ public FocusFirstItemSpinner(Context context, AttributeSet attrs,
+ int defStyle, int mode) {
+ super(context, attrs, defStyle, mode);
+ }
+
+ public FocusFirstItemSpinner(Context context, AttributeSet attrs,
+ int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public FocusFirstItemSpinner(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FocusFirstItemSpinner(Context context, int mode) {
+ super(context, mode);
+ }
+
+ public FocusFirstItemSpinner(Context context) {
+ super(context);
+ }
+
+ @Override
+ public int getSelectedItemPosition() {
+ if (!mToggleFlag) {
+ return 0;
+ }
+ return super.getSelectedItemPosition();
+ }
+
+ @Override
+ public boolean performClick() {
+ mToggleFlag = false;
+ boolean result = super.performClick();
+ mToggleFlag = true;
+ return result;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
index 6a51085f3..63afb1e8b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
@@ -113,11 +113,6 @@ public class CertifyKeySpinner extends KeySpinner {
@Override
boolean isItemEnabled(Cursor cursor) {
- // "none" entry is always enabled!
- if (cursor.getPosition() == 0) {
- return true;
- }
-
if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) {
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java
index 49b37692c..55d5aec0c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java
@@ -23,15 +23,11 @@ import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.AttributeSet;
-import android.util.Patterns;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
-import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.ContactHelper;
-import java.util.regex.Matcher;
-
public class EmailEditText extends AppCompatAutoCompleteTextView {
public EmailEditText(Context context) {
@@ -70,20 +66,7 @@ public class EmailEditText extends AppCompatAutoCompleteTextView {
@Override
public void afterTextChanged(Editable editable) {
- String email = editable.toString();
- if (email.length() > 0) {
- Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
- if (emailMatcher.matches()) {
- EmailEditText.this.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.ic_stat_retyped_ok, 0);
- } else {
- EmailEditText.this.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.ic_stat_retyped_bad, 0);
- }
- } else {
- // remove drawable if email is empty
- EmailEditText.this.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
- }
+
}
};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
index f98fda56f..a5d807313 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
@@ -171,4 +171,22 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView<KeyItem>
mLoaderManager.restartLoader(0, args, this);
}
+ @Override
+ public boolean enoughToFilter() {
+ return true;
+ }
+
+ public void showAllKeys(){
+ Bundle args = new Bundle();
+ args.putString(ARG_QUERY, "");
+ mLoaderManager.restartLoader(0, args, this);
+ super.showDropDown();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ // increase width to include add button
+ this.setDropDownWidth(this.getRight());
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
index 8fb9e38aa..0c2d93ad9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
@@ -72,11 +72,6 @@ public class SignKeySpinner extends KeySpinner {
@Override
boolean isItemEnabled(Cursor cursor) {
- // "none" entry is always enabled!
- if (cursor.getPosition() == 0) {
- return true;
- }
-
if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) {
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
index 48f10d4b9..5ffce9f24 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
@@ -18,12 +18,15 @@
package org.sufficientlysecure.keychain.util;
public class Choice <E> {
+
private String mName;
private E mId;
+ private String mDescription;
- public Choice(E id, String name) {
+ public Choice(E id, String name, String description) {
mId = id;
mName = name;
+ mDescription = description;
}
public E getId() {
@@ -34,6 +37,10 @@ public class Choice <E> {
return mName;
}
+ public String getDescription() {
+ return mDescription;
+ }
+
@Override
public String toString() {
return mName;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java
index 106775201..62dd87baa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java
@@ -20,11 +20,13 @@ package org.sufficientlysecure.keychain.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
@@ -223,6 +225,31 @@ public class FileHelper {
}
}
+ public static boolean isEncryptedFile(Context context, Uri uri) throws IOException {
+ boolean isEncrypted = false;
+
+ BufferedReader br = null;
+ try {
+ InputStream is = context.getContentResolver().openInputStream(uri);
+ br = new BufferedReader(new InputStreamReader(is));
+
+ String header = "-----BEGIN PGP MESSAGE-----";
+ int length = header.length();
+ char[] buffer = new char[length];
+ if (br.read(buffer, 0, length) == length) {
+ isEncrypted = new String(buffer).equals(header);
+ }
+ } finally {
+ try {
+ if (br != null)
+ br.close();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error closing file", e);
+ }
+ }
+ return isEncrypted;
+ }
+
public static String readableFileSize(long size) {
if (size <= 0) return "0";
final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OkHttpClientFactory.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OkHttpClientFactory.java
new file mode 100644
index 000000000..ea2ae8368
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OkHttpClientFactory.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 Michał Kępkowski
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.util;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.URL;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.CertificatePinner;
+import okhttp3.OkHttpClient;
+
+public class OkHttpClientFactory {
+ private static OkHttpClient client;
+
+ public static OkHttpClient getSimpleClient() {
+ if (client == null) {
+ client = new OkHttpClient.Builder()
+ .connectTimeout(5000, TimeUnit.MILLISECONDS)
+ .readTimeout(25000, TimeUnit.MILLISECONDS)
+ .build();
+ }
+ return client;
+ }
+
+ public static OkHttpClient getSimpleClientPinned(CertificatePinner pinner) {
+ return new OkHttpClient.Builder()
+ .connectTimeout(5000, TimeUnit.MILLISECONDS)
+ .readTimeout(25000, TimeUnit.MILLISECONDS)
+ .certificatePinner(pinner)
+ .build();
+ }
+
+ public static OkHttpClient getClientPinnedIfAvailable(URL url, Proxy proxy) throws IOException,
+ TlsHelper.TlsHelperException {
+ OkHttpClient.Builder builder = new OkHttpClient.Builder();
+
+ // don't follow any redirects for keyservers, as discussed in the security audit
+ builder.followRedirects(false)
+ .followSslRedirects(false);
+
+ if (proxy != null) {
+ // set proxy and higher timeouts for Tor
+ builder.proxy(proxy);
+ builder.connectTimeout(30000, TimeUnit.MILLISECONDS)
+ .readTimeout(45000, TimeUnit.MILLISECONDS);
+ } else {
+ builder.connectTimeout(5000, TimeUnit.MILLISECONDS)
+ .readTimeout(25000, TimeUnit.MILLISECONDS);
+ }
+
+ // If a pinned cert is available, use it!
+ // NOTE: this fails gracefully back to "no pinning" if no cert is available.
+ if (url != null && TlsHelper.getPinnedSslSocketFactory(url) != null) {
+ builder.sslSocketFactory(TlsHelper.getPinnedSslSocketFactory(url));
+ }
+
+ return builder.build();
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OkHttpKeybaseClient.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OkHttpKeybaseClient.java
index d2c90cfcd..8d3eb6963 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OkHttpKeybaseClient.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OkHttpKeybaseClient.java
@@ -17,55 +17,42 @@
package org.sufficientlysecure.keychain.util;
-import com.squareup.okhttp.OkHttpClient;
-import com.squareup.okhttp.OkUrlFactory;
+
import com.textuality.keybase.lib.KeybaseUrlConnectionClient;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+
import org.sufficientlysecure.keychain.Constants;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
-import java.net.URLConnection;
-import java.util.concurrent.TimeUnit;
/**
* Wrapper for Keybase Lib
*/
public class OkHttpKeybaseClient implements KeybaseUrlConnectionClient {
- private OkUrlFactory generateUrlFactory() {
- OkHttpClient client = new OkHttpClient();
- return new OkUrlFactory(client);
- }
-
@Override
- public URLConnection openConnection(URL url, Proxy proxy, boolean isKeybase) throws IOException {
- OkUrlFactory factory = generateUrlFactory();
- if (proxy != null) {
- factory.client().setProxy(proxy);
- factory.client().setConnectTimeout(30000, TimeUnit.MILLISECONDS);
- factory.client().setReadTimeout(40000, TimeUnit.MILLISECONDS);
- } else {
- factory.client().setConnectTimeout(5000, TimeUnit.MILLISECONDS);
- factory.client().setReadTimeout(25000, TimeUnit.MILLISECONDS);
- }
-
- factory.client().setFollowSslRedirects(false);
-
- // forced the usage of api.keybase.io pinned certificate
- if (isKeybase) {
- try {
- if (!TlsHelper.usePinnedCertificateIfAvailable(factory.client(), url)) {
- throw new IOException("no pinned certificate found for URL!");
- }
- } catch (TlsHelper.TlsHelperException e) {
- Log.e(Constants.TAG, "TlsHelper failed", e);
- throw new IOException("TlsHelper failed");
+ public Response getUrlResponse(URL url, Proxy proxy, boolean isKeybase) throws IOException {
+ OkHttpClient client = null;
+
+ try {
+ if (proxy != null) {
+ client = OkHttpClientFactory.getClientPinnedIfAvailable(url, proxy);
+ } else {
+ client = OkHttpClientFactory.getSimpleClient();
}
+ } catch (TlsHelper.TlsHelperException e) {
+ Log.e(Constants.TAG, "TlsHelper failed", e);
+ throw new IOException("TlsHelper failed");
}
- return factory.open(url);
+ Request request = new Request.Builder()
+ .url(url).build();
+ okhttp3.Response okResponse = client.newCall(request).execute();
+ return new Response(okResponse.body().byteStream(), okResponse.code(), okResponse.message(), okResponse.headers().toMultimap());
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
index b3d679a0e..5f53845d8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
@@ -19,20 +19,9 @@
package org.sufficientlysecure.keychain.util;
-import java.io.Serializable;
-import java.net.Proxy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-
+import android.accounts.Account;
import android.annotation.SuppressLint;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Parcel;
@@ -42,9 +31,23 @@ import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.Pref;
+import org.sufficientlysecure.keychain.KeychainApplication;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService;
+import java.io.Serializable;
+import java.net.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
/**
* Singleton Implementation of a Preference Helper
*/
@@ -76,9 +79,8 @@ public class Preferences {
/**
* Makes android's preference framework write to our file instead of default.
- * This allows us to use the "persistent" attribute to simplify code, which automatically
+ * This allows us to use the xml "persistent" attribute to simplify code, which automatically
* writes and reads preference values.
- * @param manager
*/
public static void setPreferenceManagerFileAndMode(PreferenceManager manager) {
manager.setSharedPreferencesName(PREF_FILE_NAME);
@@ -302,6 +304,23 @@ public class Preferences {
}
+ /**
+ * @return true if a periodic sync exists and is set to run automatically, false otherwise
+ */
+ public static boolean getKeyserverSyncEnabled(Context context) {
+ Account account = KeychainApplication.createAccountIfNecessary(context);
+
+ if (account == null) {
+ // if the account could not be created for some reason, we can't have a sync
+ return false;
+ }
+
+ String authority = Constants.PROVIDER_AUTHORITY;
+
+ return ContentResolver.getSyncAutomatically(account, authority) &&
+ !ContentResolver.getPeriodicSyncs(account, authority).isEmpty();
+ }
+
public CacheTTLPrefs getPassphraseCacheTtl() {
Set<String> pref = mSharedPreferences.getStringSet(Constants.Pref.PASSPHRASE_CACHE_TTLS, null);
if (pref == null) {
@@ -424,6 +443,12 @@ public class Preferences {
};
}
+ // sync preferences
+
+ public boolean getWifiOnlySync() {
+ return mSharedPreferences.getBoolean(Pref.ENABLE_WIFI_SYNC_ONLY, true);
+ }
+
// experimental prefs
public boolean getExperimentalEnableWordConfirm() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java
index 1492abdeb..fe62eff55 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java
@@ -19,8 +19,6 @@ package org.sufficientlysecure.keychain.util;
import android.content.res.AssetManager;
-import com.squareup.okhttp.OkHttpClient;
-
import org.sufficientlysecure.keychain.Constants;
import java.io.ByteArrayInputStream;
@@ -39,16 +37,11 @@ import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
public class TlsHelper {
- public static class TlsHelperException extends Exception {
- public TlsHelperException(Exception e) {
- super(e);
- }
- }
-
private static Map<String, byte[]> sPinnedCertificates = new HashMap<>();
/**
@@ -80,30 +73,29 @@ public class TlsHelper {
* @throws TlsHelperException
* @throws IOException
*/
- public static boolean usePinnedCertificateIfAvailable(OkHttpClient client, URL url) throws TlsHelperException, IOException {
+ public static SSLSocketFactory getPinnedSslSocketFactory(URL url) throws TlsHelperException, IOException {
if (url.getProtocol().equals("https")) {
// use certificate PIN from assets if we have one
for (String host : sPinnedCertificates.keySet()) {
if (url.getHost().endsWith(host)) {
- pinCertificate(sPinnedCertificates.get(host), client);
- return true;
+ return pinCertificate(sPinnedCertificates.get(host));
}
}
}
- return false;
+ return null;
}
/**
- * Modifies the client to accept only requests with a given certificate. Applies to all URLs requested by the
- * client.
- * Therefore a client that is pinned this way should be used to only make requests to URLs with passed certificate.
+ * Modifies the builder to accept only requests with a given certificate.
+ * Applies to all URLs requested by the builder.
+ * Therefore a builder that is pinned this way should be used to only make requests
+ * to URLs with passed certificate.
*
* @param certificate certificate to pin
- * @param client OkHttpClient to enforce pinning on
* @throws TlsHelperException
* @throws IOException
*/
- private static void pinCertificate(byte[] certificate, OkHttpClient client)
+ private static SSLSocketFactory pinCertificate(byte[] certificate)
throws TlsHelperException, IOException {
// We don't use OkHttp's CertificatePinner since it can not be used to pin self-signed
// certificate if such certificate is not accepted by TrustManager.
@@ -130,10 +122,16 @@ public class TlsHelper {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
- client.setSslSocketFactory(context.getSocketFactory());
+ return context.getSocketFactory();
} catch (CertificateException | KeyStoreException | KeyManagementException | NoSuchAlgorithmException e) {
throw new TlsHelperException(e);
}
}
+ public static class TlsHelperException extends Exception {
+ public TlsHelperException(Exception e) {
+ super(e);
+ }
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/UsbConnectionDispatcher.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/UsbConnectionDispatcher.java
new file mode 100644
index 000000000..60fc84dba
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/UsbConnectionDispatcher.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.util;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.securitytoken.UsbTransport;
+import org.sufficientlysecure.keychain.ui.UsbEventReceiverActivity;
+
+public class UsbConnectionDispatcher {
+ private Activity mActivity;
+
+ private OnDiscoveredUsbDeviceListener mListener;
+ private UsbManager mUsbManager;
+ /**
+ * Receives broadcast when a supported USB device get permission.
+ */
+ private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ switch (action) {
+ case UsbEventReceiverActivity.ACTION_USB_PERMISSION: {
+ android.hardware.usb.UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED,
+ false);
+ if (permission) {
+ Log.d(Constants.TAG, "Got permission for " + usbDevice.getDeviceName());
+ mListener.usbDeviceDiscovered(usbDevice);
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ public UsbConnectionDispatcher(final Activity activity, final OnDiscoveredUsbDeviceListener listener) {
+ this.mActivity = activity;
+ this.mListener = listener;
+ this.mUsbManager = (UsbManager) activity.getSystemService(Context.USB_SERVICE);
+ }
+
+ public void onStart() {
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(UsbEventReceiverActivity.ACTION_USB_PERMISSION);
+
+ mActivity.registerReceiver(mUsbReceiver, intentFilter);
+ }
+
+ public void onStop() {
+ mActivity.unregisterReceiver(mUsbReceiver);
+ }
+
+ /**
+ * Rescans devices and triggers {@link OnDiscoveredUsbDeviceListener}
+ */
+ public void rescanDevices() {
+ // Note: we don't check devices VID/PID because
+ // we check for permisssion instead.
+ // We should have permission only for matching devices
+ for (UsbDevice device : mUsbManager.getDeviceList().values()) {
+ if (mUsbManager.hasPermission(device)) {
+ if (mListener != null) {
+ mListener.usbDeviceDiscovered(device);
+ }
+ break;
+ }
+ }
+ }
+
+ public interface OnDiscoveredUsbDeviceListener {
+ void usbDeviceDiscovered(UsbDevice usbDevice);
+ }
+}
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..f625ba425
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..6cf9d044a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..1c30b6f22
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..672a9c96c
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..5be101001
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/layout-land/backup_code_fragment.xml b/OpenKeychain/src/main/res/layout-land/backup_code_fragment.xml
deleted file mode 100644
index 0a4f97d53..000000000
--- a/OpenKeychain/src/main/res/layout-land/backup_code_fragment.xml
+++ /dev/null
@@ -1,199 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:custom="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingTop="20dp">
-
- <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
- android:id="@+id/title_animator"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:inAnimation="@anim/fade_in"
- android:outAnimation="@anim/fade_out"
- custom:initialView="0">
-
- <TextView
- style="?android:textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:gravity="center_horizontal"
- android:padding="10dp"
- android:text="@string/backup_code_explanation" />
-
- <TextView
- style="?android:textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:gravity="center_horizontal"
- android:padding="10dp"
- android:text="@string/backup_code_enter" />
-
- <TextView
- style="?android:textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:gravity="center_horizontal"
- android:padding="10dp"
- android:text="@string/backup_code_ok" />
-
- </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
-
- <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
- android:id="@+id/code_animator"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="15dp"
- android:layout_marginTop="15dp"
- android:inAnimation="@anim/fade_in"
- android:outAnimation="@anim/fade_out"
- custom:initialView="1">
-
- <TextView
- android:id="@+id/backup_code_display"
- style="@android:style/Widget.EditText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:clickable="false"
- android:focusable="false"
- android:singleLine="true"
- android:textSize="18dp"
- android:textStyle="bold"
- android:typeface="monospace"
- tools:ignore="SpUsage"
- tools:text="AAAA-AAAA-AAAA-AAAA-AAAA-AAAA" />
-
- <com.github.pinball83.maskededittext.MaskedEditText
- android:id="@+id/backup_code_input"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:textSize="18dp"
- android:textStyle="bold"
- android:typeface="monospace"
- app:deleteChar="\u00a0"
- app:mask="****-****-****-****-****-****"
- app:maskIconColor="@color/colorPrimary"
- app:notMaskedSymbol="*"
- app:replacementChar="\u00a0"
- tools:ignore="SpUsage" />
-
- </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
-
- <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
- android:id="@+id/status_animator"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:inAnimation="@anim/fade_in_delayed"
- android:outAnimation="@anim/fade_out"
- custom:initialView="2">
-
- <Button
- android:id="@+id/button_backup_input"
- style="?android:buttonBarButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_margin="10dp"
- android:drawableLeft="@drawable/ic_mode_edit_grey_24dp"
- android:drawablePadding="8dp"
- android:padding="12dp"
- android:text="@string/btn_code_wrotedown" />
-
- <Space
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:orientation="horizontal">
-
- <TextView
- style="?android:textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:text="@string/backup_code_wrong" />
-
- <Button
- android:id="@+id/button_backup_back"
- style="?android:buttonBarButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_margin="10dp"
- android:drawableLeft="@drawable/ic_repeat_grey_24dp"
- android:drawablePadding="8dp"
- android:padding="12dp"
- android:text="@string/btn_backup_back" />
-
- </LinearLayout>
-
- <LinearLayout
- style="?android:buttonBarStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:orientation="vertical">
-
- <LinearLayout
- style="?android:buttonBarStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal">
-
- <Button
- android:id="@+id/button_backup_share"
- style="?android:buttonBarButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- android:layout_weight="1"
- android:drawableLeft="@drawable/ic_share_grey_24dp"
- android:drawablePadding="8dp"
- android:padding="12dp"
- android:text="@string/btn_backup_share" />
-
- <Button
- android:id="@+id/button_backup_save"
- style="?android:buttonBarButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- android:layout_weight="1"
- android:drawableLeft="@drawable/ic_save_grey_24dp"
- android:drawablePadding="8dp"
- android:padding="12dp"
- android:text="@string/btn_backup_save" />
-
-
- </LinearLayout>
-
- <Button
- android:id="@+id/button_faq"
- style="?android:buttonBarButtonStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:text="@string/how_to_import" />
- </LinearLayout>
-
- </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
-
-</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml b/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml
new file mode 100644
index 000000000..407fa2d9e
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml
@@ -0,0 +1,459 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingTop="20dp">
+
+ <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
+ android:id="@+id/title_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out"
+ custom:initialView="0">
+
+ <TextView
+ style="?android:textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:text="@string/backup_code_explanation" />
+
+ <TextView
+ style="?android:textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:text="@string/backup_code_enter" />
+
+ <TextView
+ style="?android:textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:text="@string/backup_code_ok" />
+
+ </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
+
+ <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
+ android:id="@+id/code_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="15dp"
+ android:layout_marginTop="15dp"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out"
+ custom:initialView="1">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal">
+
+ <TextView
+ android:id="@+id/backup_code_display_1"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="ABCD" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_2"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="EFGH" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_3"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="IJKL" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_4"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:singleLine="true"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="MNOP" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_5"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:singleLine="true"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="QRST" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_6"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:singleLine="true"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="UVWX" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal">
+
+ <!--
+ The most reliable way to correctly size these I found was to put a transparent hint on them.
+ Theoretically, this should be what the android:ems attribute is for - didn't work for me.
+ -->
+ <EditText
+ android:id="@+id/backup_code_1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ </LinearLayout>
+
+ </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
+
+ <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
+ android:id="@+id/status_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:inAnimation="@anim/fade_in_delayed"
+ android:outAnimation="@anim/fade_out"
+ custom:initialView="2">
+
+ <Button
+ android:id="@+id/button_backup_input"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_margin="10dp"
+ android:drawableLeft="@drawable/ic_mode_edit_grey_24dp"
+ android:drawablePadding="8dp"
+ android:padding="12dp"
+ android:text="@string/btn_code_wrotedown" />
+
+ <Space
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ style="?android:textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/backup_code_wrong" />
+
+ <Button
+ android:id="@+id/button_backup_back"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_margin="10dp"
+ android:drawableLeft="@drawable/ic_repeat_grey_24dp"
+ android:drawablePadding="8dp"
+ android:padding="12dp"
+ android:text="@string/btn_backup_back" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ style="?android:buttonBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <LinearLayout
+ style="?android:buttonBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal">
+
+ <Button
+ android:id="@+id/button_backup_share"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:layout_weight="1"
+ android:drawableLeft="@drawable/ic_share_grey_24dp"
+ android:drawablePadding="8dp"
+ android:padding="12dp"
+ android:text="@string/btn_backup_share" />
+
+ <Button
+ android:id="@+id/button_backup_save"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:layout_weight="1"
+ android:drawableLeft="@drawable/ic_save_grey_24dp"
+ android:drawablePadding="8dp"
+ android:padding="12dp"
+ android:text="@string/btn_backup_save" />
+
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/button_faq"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/how_to_import" />
+ </LinearLayout>
+
+ </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
+
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout-w400dp/passphrase_dialog_backup_code.xml b/OpenKeychain/src/main/res/layout-w400dp/passphrase_dialog_backup_code.xml
new file mode 100644
index 000000000..b2125d389
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout-w400dp/passphrase_dialog_backup_code.xml
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="14dp"
+ android:paddingTop="14dp">
+
+ <LinearLayout
+ android:id="@+id/input"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/passphrase_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="24dp"
+ android:text="@string/passphrase_for_backup"
+ android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp">
+
+ <!--
+ The most reliable way to correctly size these I found was to put a transparent hint on them.
+ Theoretically, this should be what the android:ems attribute is for - didn't work for me.
+ -->
+ <EditText
+ android:id="@+id/backup_code_1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:orientation="horizontal"
+ android:visibility="invisible">
+
+ <ProgressBar
+ style="?android:attr/progressBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal" />
+
+ <TextView
+ style="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:padding="4dp"
+ android:text="@string/label_unlock" />
+
+ </LinearLayout>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout-w600dp/backup_code_fragment.xml b/OpenKeychain/src/main/res/layout-w600dp/backup_code_fragment.xml
new file mode 100644
index 000000000..b247eeb74
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout-w600dp/backup_code_fragment.xml
@@ -0,0 +1,459 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingTop="20dp">
+
+ <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
+ android:id="@+id/title_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out"
+ custom:initialView="0">
+
+ <TextView
+ style="?android:textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:text="@string/backup_code_explanation" />
+
+ <TextView
+ style="?android:textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:text="@string/backup_code_enter" />
+
+ <TextView
+ style="?android:textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:text="@string/backup_code_ok" />
+
+ </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
+
+ <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
+ android:id="@+id/code_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="15dp"
+ android:layout_marginTop="15dp"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out"
+ custom:initialView="1">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal">
+
+ <TextView
+ android:id="@+id/backup_code_display_1"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="ABCD" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_2"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="EFGH" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_3"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="IJKL" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_4"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:singleLine="true"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="MNOP" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_5"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:singleLine="true"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="QRST" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_6"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:singleLine="true"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="UVWX" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal">
+
+ <!--
+ The most reliable way to correctly size these I found was to put a transparent hint on them.
+ Theoretically, this should be what the android:ems attribute is for - didn't work for me.
+ -->
+ <EditText
+ android:id="@+id/backup_code_1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ </LinearLayout>
+
+ </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
+
+ <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
+ android:id="@+id/status_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:inAnimation="@anim/fade_in_delayed"
+ android:outAnimation="@anim/fade_out"
+ custom:initialView="2">
+
+ <Button
+ android:id="@+id/button_backup_input"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_margin="10dp"
+ android:drawableLeft="@drawable/ic_mode_edit_grey_24dp"
+ android:drawablePadding="8dp"
+ android:padding="12dp"
+ android:text="@string/btn_code_wrotedown" />
+
+ <Space
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:orientation="horizontal">
+
+ <TextView
+ style="?android:textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/backup_code_wrong" />
+
+ <Button
+ android:id="@+id/button_backup_back"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_margin="10dp"
+ android:drawableLeft="@drawable/ic_repeat_grey_24dp"
+ android:drawablePadding="8dp"
+ android:padding="12dp"
+ android:text="@string/btn_backup_back" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ style="?android:buttonBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <LinearLayout
+ style="?android:buttonBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal">
+
+ <Button
+ android:id="@+id/button_backup_share"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:layout_weight="1"
+ android:drawableLeft="@drawable/ic_share_grey_24dp"
+ android:drawablePadding="8dp"
+ android:padding="12dp"
+ android:text="@string/btn_backup_share" />
+
+ <Button
+ android:id="@+id/button_backup_save"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:layout_weight="1"
+ android:drawableLeft="@drawable/ic_save_grey_24dp"
+ android:drawablePadding="8dp"
+ android:padding="12dp"
+ android:text="@string/btn_backup_save" />
+
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/button_faq"
+ style="?android:buttonBarButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/how_to_import" />
+ </LinearLayout>
+
+ </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
+
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout-land/security_token_operation_activity.xml b/OpenKeychain/src/main/res/layout-w600dp/security_token_operation_activity.xml
index cf0edac8c..cf0edac8c 100644
--- a/OpenKeychain/src/main/res/layout-land/security_token_operation_activity.xml
+++ b/OpenKeychain/src/main/res/layout-w600dp/security_token_operation_activity.xml
diff --git a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml
index 4b5058a81..b232ed423 100644
--- a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml
+++ b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml
@@ -13,147 +13,68 @@
android:paddingRight="24dp"
android:stretchColumns="1">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="4dp"
- android:text="@string/key_creation_el_gamal_info" />
-
<TableRow>
-
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:text="@string/label_algorithm" />
+ android:paddingRight="10dp"
+ android:text="@string/label_key_type" />
- <Spinner
- android:id="@+id/add_subkey_algorithm"
+ <!-- custom spinner for fixing focus on first item in list at all times -->
+ <org.sufficientlysecure.keychain.ui.util.spinner.FocusFirstItemSpinner
+ android:id="@+id/add_subkey_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="4dp" />
- </TableRow>
-
- <TableRow android:id="@+id/add_subkey_row_size">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_key_size" />
-
- <Spinner
- android:id="@+id/add_subkey_size"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="right"
+ android:dropDownWidth="wrap_content"
android:padding="4dp" />
</TableRow>
<TableRow
- android:id="@+id/add_subkey_row_curve"
- android:visibility="gone">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_ecc_curve"/>
-
- <Spinner
- android:id="@+id/add_subkey_curve"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="right"
- android:padding="4dp"/>
- </TableRow>
-
- <TextView
- android:id="@+id/add_subkey_custom_key_size_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/key_size_custom_info"
- android:visibility="gone" />
-
- <EditText
- android:id="@+id/add_subkey_custom_key_size_input"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:inputType="number"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/add_subkey_custom_key_size_info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
-
- <TableRow>
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp">
<TextView
android:id="@+id/label_usage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
+ android:layout_gravity="center_vertical|top"
+ android:paddingRight="10dp"
android:text="@string/label_usage" />
-
- <CheckBox
- android:id="@+id/add_subkey_flag_certify"
- android:enabled="false"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/flag_certify" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip" />
-
- <CheckBox
- android:id="@+id/add_subkey_flag_sign"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/flag_sign" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip" />
-
- <CheckBox
- android:id="@+id/add_subkey_flag_encrypt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/flag_encrypt" />
+ <RadioGroup
+ android:id="@+id/add_subkey_usage_group"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+
+ <RadioButton
+ android:id="@+id/add_subkey_usage_none"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:text="@string/usage_none" />
+
+ <RadioButton
+ android:id="@+id/add_subkey_usage_sign"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/usage_sign" />
+
+ <RadioButton
+ android:id="@+id/add_subkey_usage_encrypt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/usage_encrypt" />
+
+ <RadioButton
+ android:id="@+id/add_subkey_usage_sign_and_encrypt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/usage_sign_and_encrypt" />
+ </RadioGroup>
</TableRow>
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip" />
-
- <CheckBox
- android:id="@+id/add_subkey_flag_authenticate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/flag_authenticate" />
- </TableRow>
<TableRow
android:layout_marginTop="8dp"
@@ -164,7 +85,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
+ android:paddingRight="10dp"
android:text="@string/label_expiry" />
<CheckBox
diff --git a/OpenKeychain/src/main/res/layout/backup_code_fragment.xml b/OpenKeychain/src/main/res/layout/backup_code_fragment.xml
index 330f18d1c..ecdaff5cc 100644
--- a/OpenKeychain/src/main/res/layout/backup_code_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/backup_code_fragment.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -57,39 +56,330 @@
android:outAnimation="@anim/fade_out"
custom:initialView="1">
- <TextView
- android:id="@+id/backup_code_display"
- style="@android:style/Widget.EditText"
+ <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:clickable="false"
- android:focusable="false"
- android:singleLine="true"
- android:textSize="18dp"
- android:textStyle="bold"
- android:typeface="monospace"
- tools:ignore="SpUsage"
- tools:text="AAAA-AAAA-AAAA-AAAA-AAAA-AAAA" />
-
- <com.github.pinball83.maskededittext.MaskedEditText
- android:id="@+id/backup_code_input"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/backup_code_display_1"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="ABCD" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_2"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="EFGH" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_3"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="IJKL" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_4"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:singleLine="true"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="MNOP" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_5"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:singleLine="true"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="QRST" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:id="@+id/backup_code_display_6"
+ style="@android:style/Widget.EditText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="false"
+ android:focusable="false"
+ android:singleLine="true"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="SpUsage"
+ tools:text="UVWX" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:textSize="18dp"
- android:textStyle="bold"
- android:typeface="monospace"
- app:deleteChar="\u00a0"
- app:mask="****-****-****-****-****-****"
- app:maskIconColor="@color/colorPrimary"
- app:notMaskedSymbol="*"
- app:replacementChar="\u00a0"
- tools:ignore="SpUsage" />
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:orientation="horizontal">
+
+ <!--
+ The most reliable way to correctly size these I found was to put a transparent hint on them.
+ Theoretically, this should be what the android:ems attribute is for - didn't work for me.
+ -->
+ <EditText
+ android:id="@+id/backup_code_1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="6"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="6"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="6"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="6"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="6"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ </LinearLayout>
+ </LinearLayout>
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
diff --git a/OpenKeychain/src/main/res/layout/certify_item.xml b/OpenKeychain/src/main/res/layout/certify_item.xml
index 71838c2fc..8aff5823e 100644
--- a/OpenKeychain/src/main/res/layout/certify_item.xml
+++ b/OpenKeychain/src/main/res/layout/certify_item.xml
@@ -13,7 +13,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:clickable="true"
- android:layout_marginLeft="8dip"
android:layout_marginTop="8dip"/>
<LinearLayout android:id="@+id/user_id_body"
@@ -21,7 +20,6 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:singleLine="true"
- android:layout_marginLeft="8dip"
android:layout_marginTop="4dip">
<CheckBox
diff --git a/OpenKeychain/src/main/res/layout/certify_key_fragment.xml b/OpenKeychain/src/main/res/layout/certify_key_fragment.xml
index 23685ce15..01837240b 100644
--- a/OpenKeychain/src/main/res/layout/certify_key_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/certify_key_fragment.xml
@@ -22,8 +22,9 @@
android:id="@+id/textView"
android:layout_weight="1" />
- <org.sufficientlysecure.keychain.ui.widget.FixedListView
- android:id="@+id/view_key_user_ids"
+ <fragment
+ android:id="@+id/multi_user_ids_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.MultiUserIdsFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
diff --git a/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
index 46b5d186d..dbd53db08 100644
--- a/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
@@ -39,7 +39,9 @@
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:focusable="true"
+ android:focusableInTouchMode="true" />
<TextView
android:layout_width="match_parent"
@@ -54,6 +56,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp" />
diff --git a/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml b/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml
index 3245fd28b..f81472cf8 100644
--- a/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml
@@ -39,13 +39,21 @@
<org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView
android:id="@+id/recipient_list"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
android:hint="@string/label_to"
android:minHeight="56dip"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
+ <ImageView
+ android:id="@+id/add_recipient"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:clickable="true"
+ android:src="@drawable/ic_person_add_grey_24dp"/>
</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/multi_user_ids_fragment.xml b/OpenKeychain/src/main/res/layout/multi_user_ids_fragment.xml
new file mode 100644
index 000000000..560934f50
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/multi_user_ids_fragment.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<org.sufficientlysecure.keychain.ui.widget.FixedListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/view_key_user_ids"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
diff --git a/OpenKeychain/src/main/res/layout/passphrase_dialog_backup_code.xml b/OpenKeychain/src/main/res/layout/passphrase_dialog_backup_code.xml
index b17c9dba7..1eb55b3f6 100644
--- a/OpenKeychain/src/main/res/layout/passphrase_dialog_backup_code.xml
+++ b/OpenKeychain/src/main/res/layout/passphrase_dialog_backup_code.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -26,24 +25,168 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
- android:layout_marginTop="8dp">
+ android:layout_marginTop="8dp"
+ android:orientation="vertical">
- <com.github.pinball83.maskededittext.MaskedEditText
- android:id="@+id/backup_code"
+ <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:textSize="18dp"
- android:textStyle="bold"
- android:typeface="monospace"
- app:deleteChar="\u00a0"
- app:mask="****-****-****-****-****-****"
- app:maskIconColor="@color/colorPrimary"
- app:notMaskedSymbol="*"
- app:replacementChar="\u00a0"
- tools:ignore="SpUsage" />
+ android:layout_gravity="right"
+ android:orientation="horizontal">
+
+ <!--
+ The most reliable way to correctly size these I found was to put a transparent hint on them.
+ Theoretically, this should be what the android:ems attribute is for - didn't work for me.
+ -->
+ <EditText
+ android:id="@+id/backup_code_1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="-"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ <EditText
+ android:id="@+id/backup_code_6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="ABCD"
+ android:inputType="textNoSuggestions|textCapCharacters"
+ android:maxLength="4"
+ android:singleLine="true"
+ android:textColorHint="@android:color/transparent"
+ android:textSize="18dp"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ tools:ignore="HardcodedText,SpUsage" />
+
+ </LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/two_line_spinner_dropdown_item.xml b/OpenKeychain/src/main/res/layout/two_line_spinner_dropdown_item.xml
new file mode 100644
index 000000000..ae325eaab
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/two_line_spinner_dropdown_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingLeft="10dp"
+ android:paddingTop="6dp"
+ android:paddingRight="10dp"
+ android:paddingBottom="6dp"
+ android:background="?android:attr/selectableItemBackground">
+
+ <TextView android:id="@android:id/text1"
+ style="?android:attr/spinnerDropDownItemStyle"
+ android:textStyle="bold"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp" />
+
+ <TextView android:id="@android:id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp"
+ android:paddingTop="2dp" />
+
+</LinearLayout>
+
+
+
+
diff --git a/OpenKeychain/src/main/res/layout/upload_key_activity.xml b/OpenKeychain/src/main/res/layout/upload_key_activity.xml
index 6a0be9ce5..6acb22c3a 100644
--- a/OpenKeychain/src/main/res/layout/upload_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/upload_key_activity.xml
@@ -41,6 +41,12 @@
android:layout_marginBottom="4dp"
android:layout_marginTop="4dp" />
+ <fragment
+ android:id="@+id/multi_user_ids_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.MultiUserIdsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<TextView
style="@style/SectionHeader"
android:layout_width="wrap_content"
diff --git a/OpenKeychain/src/main/res/raw-de/help_changelog.md b/OpenKeychain/src/main/res/raw-de/help_changelog.md
index 8b170fb76..4070b5ae1 100644
--- a/OpenKeychain/src/main/res/raw-de/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-de/help_changelog.md
@@ -2,9 +2,9 @@
## 3.9
- * Detection and handling of text data
- * Performance improvements
- * UI improvements for Security Token handling
+ * Erkennung und Bearbeitung von Textdaten
+ * Performanceverbesserungen
+ * Verbesserung der Benutzeroberfläche zur Handhabung von Smartcards
## 3.8
diff --git a/OpenKeychain/src/main/res/raw-eu/help_changelog.md b/OpenKeychain/src/main/res/raw-eu/help_changelog.md
index 08be3f6ce..1a1be91fc 100644
--- a/OpenKeychain/src/main/res/raw-eu/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-eu/help_changelog.md
@@ -2,9 +2,9 @@
## 3.9
- * Detection and handling of text data
- * Performance improvements
- * UI improvements for Security Token handling
+ * Idazki datuen atzematea eta kudeaketa
+ * Egintza hobekuntzak
+ * EI hobekuntzak Segurtasun Lekukoaren kudeaketarako
## 3.8
diff --git a/OpenKeychain/src/main/res/raw-fr/help_changelog.md b/OpenKeychain/src/main/res/raw-fr/help_changelog.md
index 096fda99d..b815516a3 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-fr/help_changelog.md
@@ -2,9 +2,9 @@
## 3.9
- * Detection and handling of text data
- * Performance improvements
- * UI improvements for Security Token handling
+ * Détection et gestion de données texte
+ * Améliorations des performances
+ * Améliorations de l'IG pour la gestion des jetons de sécurité
## 3.8
diff --git a/OpenKeychain/src/main/res/raw-it/advanced.md b/OpenKeychain/src/main/res/raw-it/advanced.md
index 54a694084..1028d0d92 100644
--- a/OpenKeychain/src/main/res/raw-it/advanced.md
+++ b/OpenKeychain/src/main/res/raw-it/advanced.md
@@ -1,9 +1,9 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: Si prega di mettere ogni frase in una propria linea, Transifex mette ogni riga nel proprio campo di traduzione!)
-Advanced screen allows you to
-* share key in non-recommended ways
-* edit identities
-* edit subkeys
-* examine certificates in detail
+La visuale avanzata permette di
+* condividere le chiavi tramite modalità non consigliate
+* modificare le identità
+* modificare le sottochiavi
+* leggere i dettagli dei certificati
-Only proceed if you know what you are doing! \ No newline at end of file
+Continua solamente se hai perfettamente capito cosa stai facendo! \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-it/help_about.md b/OpenKeychain/src/main/res/raw-it/help_about.md
index a5957475c..96a51ff18 100644
--- a/OpenKeychain/src/main/res/raw-it/help_about.md
+++ b/OpenKeychain/src/main/res/raw-it/help_about.md
@@ -2,7 +2,7 @@
[https://www.openkeychain.org](https://www.openkeychain.org)
-[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android.
+[OpenKeychain](https://www.openkeychain.org) è un'implementazione di OpenPGP in ambiente Android.
Licenza: GPLv3+
@@ -61,11 +61,11 @@ Licenza: GPLv3+
* [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
* [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
- * [OkHttp](https://square.github.io/okhttp/) (Apache License v2)
+ * [OkHttp](https://square.github.io/okhttp/) (Licenza Apache v2)
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Design materiale) (Apache License v2)
* [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 License)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (Licenza MIT X11)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
diff --git a/OpenKeychain/src/main/res/raw-it/help_changelog.md b/OpenKeychain/src/main/res/raw-it/help_changelog.md
index 55c62bbcd..100fee640 100644
--- a/OpenKeychain/src/main/res/raw-it/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-it/help_changelog.md
@@ -17,7 +17,7 @@
## 3.7
* Improved Android 6 support (permissions, integration into text selection)
- * API: Version 10
+ * API: Versione 10
## 3.6
@@ -30,7 +30,7 @@
* Experimental feature: link keys to Github, Twitter accounts
* Experimental feature: key confirmation via phrases
* Experimental feature: dark theme
- * API: Version 9
+ * API: Versione 9
## 3.5
@@ -38,7 +38,7 @@
* Key revocation on key deletion
* Improved checks for insecure cryptography
* Fix: Don't close OpenKeychain after first time wizard succeeds
- * API: Version 8
+ * API: Versione 8
## 3.4
@@ -62,7 +62,7 @@
* Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
- * Requires Android 4
+ * Richiede Android 4
* Redesigned key screen
* Simplify crypto preferences, better selection of secure ciphers
* API: Detached signatures, free selection of signing key,...
@@ -85,7 +85,7 @@
## 3.1
- * Fix crash on Android 5
+ * Corretto crash su Android 5
* New certify screen
* Secure Exchange directly from key list (SafeSlinger library)
* New QR Code program flow
@@ -166,7 +166,7 @@ This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2
## 2.6.1
- * Some fixes for regression bugs
+ * Correzione di bug di regressione
## 2.6
@@ -242,7 +242,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
## 2.1
- * Lots of bug fixes
+ * Correzione di molti bug
* New API for developers
* PRNG bug fix by Google
@@ -263,8 +263,8 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
* App2sd
* More choices for passphrase cache: 1, 2, 4, 8, hours
* Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
- * Bugfixes
- * Optimizations
+ * Correzione di bug
+ * Ottimizzazioni
## 1.0.7
@@ -281,7 +281,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
* Stream encryption/decryption (gallery, etc.)
* New options (language, force v3 signatures)
* Interface changes
- * Bugfixes
+ * Correzione di bug
## 1.0.5
@@ -320,7 +320,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
* K-9 Mail integration, APG supporting beta build of K-9 Mail
* Support of more file managers (including ASTRO)
- * Slovenian translation
+ * Traduzione in Sloveno
* New database, much faster, less memory usage
* Defined Intents and content provider for other apps
- * Bugfixes \ No newline at end of file
+ * Correzione di bug \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ja/help_changelog.md b/OpenKeychain/src/main/res/raw-ja/help_changelog.md
index 1c5d077f5..49e659310 100644
--- a/OpenKeychain/src/main/res/raw-ja/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-ja/help_changelog.md
@@ -2,9 +2,9 @@
## 3.9
- * Detection and handling of text data
- * Performance improvements
- * UI improvements for Security Token handling
+ * テキストデータの検出と処理
+ * パフォーマンスの改善
+ * セキュリティトークンを処理する UI の改善
## 3.8
diff --git a/OpenKeychain/src/main/res/raw-la/advanced.md b/OpenKeychain/src/main/res/raw-la/advanced.md
new file mode 100644
index 000000000..54a694084
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-la/advanced.md
@@ -0,0 +1,9 @@
+[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+
+Advanced screen allows you to
+* share key in non-recommended ways
+* edit identities
+* edit subkeys
+* examine certificates in detail
+
+Only proceed if you know what you are doing! \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-la/help_about.md b/OpenKeychain/src/main/res/raw-la/help_about.md
new file mode 100644
index 000000000..fad35b356
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-la/help_about.md
@@ -0,0 +1,72 @@
+[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+
+[https://www.openkeychain.org](https://www.openkeychain.org)
+
+[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android.
+
+License: GPLv3+
+
+[//]: # (NOTE: Alphabetic ordering)
+
+## Main Developers
+ * Dominik Schürmann (Maintainer)
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
+ * Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
+ * Brian C. Barnes
+ * Bahtiar 'kalkin' Gadimov
+ * Daniel Albert
+ * Daniel Hammann
+ * Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
+ * Greg Witczak
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
+ * Markus Doits
+ * Miroojin Bakshi
+ * Morgan Gangwere
+ * Nikhil Peter Raj
+ * Paul Sarbinowski
+ * 'Senecaso'
+ * Signe Rüsch
+ * Sreeram Boyapati
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
+
+## Libraries
+ * [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
+ * [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
+ * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](https://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 License)
+ * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [ZXing](https://github.com/zxing/zxing) (Apache License v2)
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-la/help_certification.md b/OpenKeychain/src/main/res/raw-la/help_certification.md
new file mode 100644
index 000000000..3518adf73
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-la/help_certification.md
@@ -0,0 +1,28 @@
+[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+
+## Key Confirmation
+Without confirmation, you cannot be sure if a key really corresponds to a specific person.
+The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
+To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+
+## Key Status
+
+<img src="status_signature_verified_cutout_24dp"/>
+Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
+<img src="status_signature_unverified_cutout_24dp"/>
+Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
+<img src="status_signature_expired_cutout_24dp"/>
+Expired: This key is no longer valid. Only the owner can extend its validity.
+<img src="status_signature_revoked_cutout_24dp"/>
+Revoked: This key is no longer valid. It has been revoked by its owner.
+
+## Advanced Information
+A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
+This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
+"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
+
+Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
+Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
+We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
+We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
+Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-la/help_changelog.md b/OpenKeychain/src/main/res/raw-la/help_changelog.md
new file mode 100644
index 000000000..629931b2c
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-la/help_changelog.md
@@ -0,0 +1,326 @@
+[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+
+## 3.9
+
+ * Detection and handling of text data
+ * Performance improvements
+ * UI improvements for Security Token handling
+
+
+## 3.8
+
+ * Redesigned key editing
+ * Choose remember time individually when entering passwords
+ * Facebook key import
+
+
+## 3.7
+
+ * Improved Android 6 support (permissions, integration into text selection)
+ * API: Version 10
+
+
+## 3.6
+
+ * Encrypted backups
+ * Security fixes based on external security audit
+ * YubiKey NEO key creation wizard
+ * Basic internal MIME support
+ * Automatic key synchronization
+ * Experimental feature: link keys to Github, Twitter accounts
+ * Experimental feature: key confirmation via phrases
+ * Experimental feature: dark theme
+ * API: Version 9
+
+
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
+
+## 3.2
+
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
+ * Material design
+ * Integration of QR Code Scanning (New permissions required)
+ * Improved key creation wizard
+ * Fix missing contacts after sync
+ * Requires Android 4
+ * Redesigned key screen
+ * Simplify crypto preferences, better selection of secure ciphers
+ * API: Detached signatures, free selection of signing key,...
+ * Fix: Some valid keys were shown revoked or expired
+ * Don't accept signatures by expired or revoked subkeys
+ * Keybase.io support in advanced view
+ * Method to update all keys at once
+
+
+## 3.1.2
+
+ * Fix key export to files (now for real)
+
+
+## 3.1.1
+
+ * Fix key export to files (they were written partially)
+ * Fix crash on Android 2.3
+
+
+## 3.1
+
+ * Fix crash on Android 5
+ * New certify screen
+ * Secure Exchange directly from key list (SafeSlinger library)
+ * New QR Code program flow
+ * Redesigned decrypt screen
+ * New icon usage and colors
+ * Fix import of secret keys from Symantec Encryption Desktop
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
+
+
+## 3.0.1
+
+ * Better handling of large key imports
+ * Improved subkey selection
+
+
+## 3.0
+
+ * Propose installable compatible apps in apps list
+ * New design for decryption screens
+ * Many fixes for key import, also fixes stripped keys
+ * Honor and display key authenticate flags
+ * User interface to generate custom keys
+ * Fixing user id revocation certificates
+ * New cloud search (searches over traditional keyservers and keybase.io)
+ * Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
+
+
+## 2.9.2
+
+ * Fix keys broken in 2.9.1
+ * Experimental YubiKey support: Decryption now working via API
+
+
+## 2.9.1
+
+ * Split encrypt screen into two
+ * Fix key flags handling (now supporting Mailvelope 0.7 keys)
+ * Improved passphrase handling
+ * Key sharing via SafeSlinger
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Fix usage of stripped keys
+ * SHA256 as default for compatibility
+ * Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
+ * OpenPGP API now handles revoked/expired keys and returns all user ids
+
+
+## 2.9
+
+ * Fixing crashes introduced in v2.8
+ * Experimental ECC support
+ * Experimental YubiKey support: Only signing with imported keys
+
+
+## 2.8
+
+ * So many bugs have been fixed in this release that we focus on the main new features
+ * Key edit: awesome new design, key revocation
+ * Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
+ * New first time screen
+ * New key creation screen: autocompletion of name and email based on your personal Android accounts
+ * File encryption: awesome new design, support for encrypting multiple files
+ * New icons to show status of key (by Brennan Novak)
+ * Important bug fix: Importing of large key collections from a file is now possible
+ * Notification showing cached passphrases
+ * Keys are connected to Android's contacts
+
+This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar
+
+## 2.7
+
+ * Purple! (Dominik, Vincent)
+ * New key view design (Dominik, Vincent)
+ * New flat Android buttons (Dominik, Vincent)
+ * API fixes (Dominik)
+ * Keybase.io import (Tim Bray)
+
+
+## 2.6.1
+
+ * Some fixes for regression bugs
+
+
+## 2.6
+
+ * Key certifications (thanks to Vincent Breitmoser)
+ * Support for GnuPG partial secret keys (thanks to Vincent Breitmoser)
+ * New design for signature verification
+ * Custom key length (thanks to Greg Witczak)
+ * Fix share-functionality from other apps
+
+
+## 2.5
+
+ * Fix decryption of symmetric OpenPGP messages/files
+ * Refactored key edit screen (thanks to Ash Hughes)
+ * New modern design for encrypt/decrypt screens
+ * OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
+
+
+## 2.4
+Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.
+
+ * New unified key list
+ * Colorized key fingerprint
+ * Support for keyserver ports
+ * Deactivate possibility to generate weak keys
+ * Much more internal work on the API
+ * Certify user ids
+ * Keyserver query based on machine-readable output
+ * Lock navigation drawer on tablets
+ * Suggestions for emails on creation of keys
+ * Search in public key lists
+ * And much more improvements and fixes…
+
+
+## 2.3.1
+
+ * Hotfix for crash when upgrading from old versions
+
+
+## 2.3
+
+ * Remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
+ * Fix setting expiry dates on keys (thanks to Ash Hughes)
+ * More internal fixes when editing keys (thanks to Ash Hughes)
+ * Querying keyservers directly from the import screen
+ * Fix layout and dialog style on Android 2.2-3.0
+ * Fix crash on keys with empty user ids
+ * Fix crash and empty lists when coming back from signing screen
+ * Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
+ * Fix upload of key from signing screen
+
+
+## 2.2
+
+ * New design with navigation drawer
+ * New public key list design
+ * New public key view
+ * Bug fixes for importing of keys
+ * Key cross-certification (thanks to Ash Hughes)
+ * Handle UTF-8 passwords properly (thanks to Ash Hughes)
+ * First version with new languages (thanks to the contributors on Transifex)
+ * Sharing of keys via QR Codes fixed and improved
+ * Package signature verification for API
+
+
+## 2.1.1
+
+ * API Updates, preparation for K-9 Mail integration
+
+
+## 2.1
+
+ * Lots of bug fixes
+ * New API for developers
+ * PRNG bug fix by Google
+
+
+## 2.0
+
+ * Complete redesign
+ * Share public keys via QR codes, NFC beam
+ * Sign keys
+ * Upload keys to server
+ * Fixes import issues
+ * New AIDL API
+
+
+## 1.0.8
+
+ * Basic keyserver support
+ * App2sd
+ * More choices for passphrase cache: 1, 2, 4, 8, hours
+ * Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
+ * Bugfixes
+ * Optimizations
+
+
+## 1.0.7
+
+ * Fixed problem with signature verification of texts with trailing newline
+ * More options for passphrase cache time to live (20, 40, 60 mins)
+
+
+## 1.0.6
+
+ * Account adding crash on Froyo fixed
+ * Secure file deletion
+ * Option to delete key file after import
+ * Stream encryption/decryption (gallery, etc.)
+ * New options (language, force v3 signatures)
+ * Interface changes
+ * Bugfixes
+
+
+## 1.0.5
+
+ * German and Italian translation
+ * Much smaller package, due to reduced BC sources
+ * New preferences GUI
+ * Layout adjustment for localization
+ * Signature bugfix
+
+
+## 1.0.4
+
+ * Fixed another crash caused by some SDK bug with query builder
+
+
+## 1.0.3
+
+ * Fixed crashes during encryption/signing and possibly key export
+
+
+## 1.0.2
+
+ * Filterable key lists
+ * Smarter pre-selection of encryption keys
+ * New Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
+ * Fixes and additional features (key preselection) for K-9 Mail, new beta build available
+
+
+## 1.0.1
+
+ * GMail account listing was broken in 1.0.0, fixed again
+
+
+## 1.0.0
+
+ * K-9 Mail integration, APG supporting beta build of K-9 Mail
+ * Support of more file managers (including ASTRO)
+ * Slovenian translation
+ * New database, much faster, less memory usage
+ * Defined Intents and content provider for other apps
+ * Bugfixes \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-la/help_start.md b/OpenKeychain/src/main/res/raw-la/help_start.md
new file mode 100644
index 000000000..4cc331942
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-la/help_start.md
@@ -0,0 +1,16 @@
+[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+
+## How do I activate OpenKeychain in K-9 Mail?
+To use OpenKeychain with K-9 Mail, you want to follow these steps:
+ 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
+ 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
+ 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
+
+## I found a bug in OpenKeychain!
+Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+
+## Contribute
+If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+
+## Translations
+Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-nb/advanced.md b/OpenKeychain/src/main/res/raw-nb/advanced.md
index 54a694084..53a215af6 100644
--- a/OpenKeychain/src/main/res/raw-nb/advanced.md
+++ b/OpenKeychain/src/main/res/raw-nb/advanced.md
@@ -1,9 +1,9 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTER: Vennligst putt hver setning på sin egen linje, Transifex putter hver linje i sitt eget oversettelsesfelt!)
-Advanced screen allows you to
-* share key in non-recommended ways
-* edit identities
-* edit subkeys
-* examine certificates in detail
+Avansert-skjermen lar deg
+* dele nøkler på uanbefalte måter
+* slette identiteter
+* rediger undernøkler
+* undersøk sertifikatdetaljer
-Only proceed if you know what you are doing! \ No newline at end of file
+Fortsett kun hvis du vet hva du gjør! \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-nb/help_about.md b/OpenKeychain/src/main/res/raw-nb/help_about.md
index fad35b356..9b9e496a6 100644
--- a/OpenKeychain/src/main/res/raw-nb/help_about.md
+++ b/OpenKeychain/src/main/res/raw-nb/help_about.md
@@ -4,22 +4,22 @@
[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android.
-License: GPLv3+
+Lisens: GPLv3+
-[//]: # (NOTE: Alphabetic ordering)
+[//]: # (MERK: Alfabetisk rekkefølge)
-## Main Developers
- * Dominik Schürmann (Maintainer)
+## Hovedutviklere
+ * Dominik Schürmann (Vedlikeholder)
* Vincent Breitmoser
-## Top Contributors
+## Topp-bidragsytere
* Adithya Abraham Philip
* Ash Hughes
* 'mar-v-in'
- * 'Thialfihar' (APG developer)
+ * 'Thialfihar' (APG-utvikler)
* Tim Bray
-## Occasional Contributors
+## Periodevise bidragsytere
* Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
@@ -51,10 +51,10 @@ License: GPLv3+
* Sreeram Boyapati
* 'steelman'
-[//]: # (NOTE: Alphabetic ordering)
+[//]: # (MERK: Alfabetisk rekkefølge)
-## Libraries
- * [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+## Bibliotek
+ * [Libraries for Android-støtte](http://developer.android.com/tools/support-library/index.html) (Apache-lisens v2)
* [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
* [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
diff --git a/OpenKeychain/src/main/res/raw-nb/help_certification.md b/OpenKeychain/src/main/res/raw-nb/help_certification.md
index 3518adf73..19098897e 100644
--- a/OpenKeychain/src/main/res/raw-nb/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-nb/help_certification.md
@@ -1,28 +1,28 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-## Key Confirmation
+## Oppsett av nøkkel
Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+Den enkleste måten å bekrefte en nøkkel er ved å skanne QR-koden eller bytte via NFC.
+For bekreftelse av nøkler mellom fler enn to personer seg imellom anbefaler vi nøkkelutvekslingsmetoden tilgjengelig for deres nøkler.
-## Key Status
+## Status for nøkkel
<img src="status_signature_verified_cutout_24dp"/>
-Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
+Bekreftet: Du har allerede bekreftet denne nøkkelen, eksempelvis ved å skanne QR-koden.
<img src="status_signature_unverified_cutout_24dp"/>
-Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
+Ubekreftet: Denne nøkkelen har ikke blitt bekreftet enda. Du kan ikke være sikker på at nøkkelen tilhører rett person.
<img src="status_signature_expired_cutout_24dp"/>
-Expired: This key is no longer valid. Only the owner can extend its validity.
+Utløpt: Denne nøkkelen er ikke lenger gyldig. Bare eieren kan forlenge dens gyldighet.
<img src="status_signature_revoked_cutout_24dp"/>
-Revoked: This key is no longer valid. It has been revoked by its owner.
+Utløpt: Denne nøkkelen er ikke lenger gyldig. Den har blitt tilbakekalt av eieren.
-## Advanced Information
-A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
-This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
+## Avansert informasjon
+En "nøkkel-bekreftelse" i OpenKeychain er implementert ved å opprette et sertifikat i henhold til OpenPGP-standarden.
+Denne sertifiseringen er en ["vanlig sertifisering (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) beskrevet i standarden av:
"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
+Vi antar at nøklene er bekreftet nok til at de fremdeles er brukbare nok til å kunne kjøres "på sparket".
We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-nb/help_changelog.md b/OpenKeychain/src/main/res/raw-nb/help_changelog.md
index 629931b2c..f544486f3 100644
--- a/OpenKeychain/src/main/res/raw-nb/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-nb/help_changelog.md
@@ -3,73 +3,73 @@
## 3.9
* Detection and handling of text data
- * Performance improvements
- * UI improvements for Security Token handling
+ * Ytelsesforbedringer
+ * Brukergrensesnittforbedringer for behandling av sikkerhetssymboler
## 3.8
- * Redesigned key editing
+ * Re-designet nøkkelredigering
* Choose remember time individually when entering passwords
- * Facebook key import
+ * Nøkkelimport fra Facebook
## 3.7
- * Improved Android 6 support (permissions, integration into text selection)
- * API: Version 10
+ * Forbedret Android 6 støtte (tillatelser, ingegrering i tekstfeltet)
+ * API: Versjon 10
## 3.6
- * Encrypted backups
- * Security fixes based on external security audit
+ * Krypterte sikkerhetskopier
+ * Sikkerhetsutbedringer basert på eksterne sikkerhetsgjennomganger
* YubiKey NEO key creation wizard
- * Basic internal MIME support
+ * Grunnleggende intern MIME-støtte
* Automatic key synchronization
* Experimental feature: link keys to Github, Twitter accounts
- * Experimental feature: key confirmation via phrases
- * Experimental feature: dark theme
- * API: Version 9
+ * Eksperimentell funksjon: nøkkelbekreftelse via fraser
+ * Eksperimentell funksjon: mørk drakt
+ * API: Versjon 9
## 3.5
* Key revocation on key deletion
- * Improved checks for insecure cryptography
- * Fix: Don't close OpenKeychain after first time wizard succeeds
- * API: Version 8
+ * Forbedret sjekking av usikker kryptografi
+ * Fix: Ikke lukke OpenKeychain etter at førstegangsveiviseren fullføres
+ * API: Versjon 8
## 3.4
- * Anonymous key download over Tor
- * Proxy support
+ * Anonym nøkkel-nedlasting over Tor
+ * Mellomtjener-støtte
* Better YubiKey error handling
## 3.3
- * New decryption screen
- * Decryption of multiple files at once
- * Better handling of YubiKey errors
+ * Ny dekrypteringsoversikt
+ * Dekryptering av flere filer samtidig
+ * Bedre håndtering av YubiKey-feil
## 3.2
* First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Code Scanning (New permissions required)
- * Improved key creation wizard
- * Fix missing contacts after sync
- * Requires Android 4
- * Redesigned key screen
+ * Integrasjon av QR-kodeskanning (Nye tilganger kreves)
+ * Forbedret veiviser for nøkkelopprettelse
+ * Fikser manglende kontakter etter synkronisering
+ * Krever Android 4
+ * Re-designet nøkkel-oversikt
* Simplify crypto preferences, better selection of secure ciphers
* API: Detached signatures, free selection of signing key,...
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
- * Method to update all keys at once
+ * Metode for å oppdatere alle nøklene på én gang
## 3.1.2
@@ -80,7 +80,7 @@
## 3.1.1
* Fix key export to files (they were written partially)
- * Fix crash on Android 2.3
+ * Fikser kræsj på Android 2.3
## 3.1
@@ -230,7 +230,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
* Bug fixes for importing of keys
* Key cross-certification (thanks to Ash Hughes)
* Handle UTF-8 passwords properly (thanks to Ash Hughes)
- * First version with new languages (thanks to the contributors on Transifex)
+ * Første versjon med de nye språkene (takket være bidragsytere på Transifex)
* Sharing of keys via QR Codes fixed and improved
* Package signature verification for API
@@ -286,7 +286,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
## 1.0.5
- * German and Italian translation
+ * Tysk og Italiensk oversettelse ved
* Much smaller package, due to reduced BC sources
* New preferences GUI
* Layout adjustment for localization
@@ -320,7 +320,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
* K-9 Mail integration, APG supporting beta build of K-9 Mail
* Support of more file managers (including ASTRO)
- * Slovenian translation
+ * Slovensk oversettelse ved
* New database, much faster, less memory usage
* Defined Intents and content provider for other apps
* Bugfixes \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-nb/help_start.md b/OpenKeychain/src/main/res/raw-nb/help_start.md
index 4cc331942..aa53344ff 100644
--- a/OpenKeychain/src/main/res/raw-nb/help_start.md
+++ b/OpenKeychain/src/main/res/raw-nb/help_start.md
@@ -1,16 +1,16 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-## How do I activate OpenKeychain in K-9 Mail?
+## Hvordan aktiverer jeg OpenKeychain i K-9 e-post?
To use OpenKeychain with K-9 Mail, you want to follow these steps:
1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
+ 2. Velg "Kontoinnstillinger", rull ned til bunnen og klikk "Kryptografi".
+ 3. Klikk på "OpenPGP-tilbyder" og vel OpenKeychain fra listen.
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+## Jeg fant en feil i OpenKeychain!
+Rapporter feil ved bruk av [feilrettingsoversikten i OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+## Bidra
+Hvis du ønsker å hjelpe oss å utvikle OpenKeychain ved å skrive kode [følg vår lille guide på Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
+## Oversettelser
+Hjelp til med oversettelsen av OpenKeychain! Alle kan delta på [OpenKeychain på Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-pt-rBR/advanced.md b/OpenKeychain/src/main/res/raw-pt-rBR/advanced.md
index 54a694084..6551e778e 100644
--- a/OpenKeychain/src/main/res/raw-pt-rBR/advanced.md
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/advanced.md
@@ -1,9 +1,9 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: coloque cada frase em sua própria linha, Transifex coloca cada linha em seu próprio campo de tradução!)
-Advanced screen allows you to
-* share key in non-recommended ways
-* edit identities
-* edit subkeys
-* examine certificates in detail
+Configuração avançada de tela permite que você
+compartilhar a chave de formas não recomendadas
+* editar identidades
+* editar sub-chaves
+* examine certificados em detalhes
-Only proceed if you know what you are doing! \ No newline at end of file
+Só prossiga se você sabe o que está fazendo! \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-pt-rBR/help_about.md b/OpenKeychain/src/main/res/raw-pt-rBR/help_about.md
index fad35b356..80d07423f 100644
--- a/OpenKeychain/src/main/res/raw-pt-rBR/help_about.md
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/help_about.md
@@ -1,25 +1,25 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: coloque cada frase em sua própria linha, Transifex coloca cada linha em seu próprio campo de tradução!)
[https://www.openkeychain.org](https://www.openkeychain.org)
-[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android.
+[OpenKeychain](https://www.openkeychain.org) é uma implementação OpenPGP para Android.
-License: GPLv3+
+Licença: GPLv3+
-[//]: # (NOTE: Alphabetic ordering)
+[//]: # (NOTA: Ordem alfabética)
-## Main Developers
- * Dominik Schürmann (Maintainer)
+## Desenvolvedores principais
+ * Dominik Schürmann (Mantenedor)
* Vincent Breitmoser
-## Top Contributors
+## Principais Colaboradores
* Adithya Abraham Philip
* Ash Hughes
* 'mar-v-in'
- * 'Thialfihar' (APG developer)
+ * 'Thialfihar' (APG desenvolvedor)
* Tim Bray
-## Occasional Contributors
+## Colaboradores Ocasionais
* Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
@@ -51,9 +51,9 @@ License: GPLv3+
* Sreeram Boyapati
* 'steelman'
-[//]: # (NOTE: Alphabetic ordering)
+[//]: # (NOTA: Ordem alfabética)
-## Libraries
+## Bicliotecas
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
* [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
* [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
diff --git a/OpenKeychain/src/main/res/raw-pt-rBR/help_certification.md b/OpenKeychain/src/main/res/raw-pt-rBR/help_certification.md
index 3518adf73..b3d48189c 100644
--- a/OpenKeychain/src/main/res/raw-pt-rBR/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/help_certification.md
@@ -1,28 +1,28 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: coloque cada frase em sua própria linha, Transifex coloca cada linha em seu próprio campo de tradução!)
-## Key Confirmation
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+## Confirmação de chave
+Sem confirmação, você não pode ter certeza se uma chave realmente corresponde a uma pessoa específica.
+A maneira mais simples para confirmar a chave é fazendo a leitura de um código QR ou trocá-lo via NFC.
+Para confirmar chaves entre mais de duas pessoas, sugerimos usar o método de troca de chaves disponível para suas chaves.
-## Key Status
+## Estado de chave
<img src="status_signature_verified_cutout_24dp"/>
-Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
+Confirmado: Você já confirmou esta chave, por por exemplo, fazendo a leitura de um código QR.
<img src="status_signature_unverified_cutout_24dp"/>
-Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
+Não confirmado: Esta chave ainda não foi confirmada. Você não pode ter a certeza se a chave realmente corresponde a uma pessoa específica.
<img src="status_signature_expired_cutout_24dp"/>
-Expired: This key is no longer valid. Only the owner can extend its validity.
+Expirada: Esta chave não é mais válida. Somente o proprietário pode extender a sua validade.
<img src="status_signature_revoked_cutout_24dp"/>
-Revoked: This key is no longer valid. It has been revoked by its owner.
+Revogada: Esta chave não é mais válida. Ela foi revogada pelo seu proprietário.
-## Advanced Information
-A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
-This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
+## Informações avançadas
+A "confirmação chave" no OpenKeychain é implementada através da criação de uma certificação, de acordo com o padrão OpenPGP.
+Esta certificação é uma ["certificação genérica (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) como descrito na norma:
+"O emissor deste certificado não faz qualquer afirmação em particular quanto à forma, que a certificadora verificou que o proprietário da chave é na verdade a pessoa descrita pelo ID de usuário."
-Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
-Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
+Tradicionalmente, certificações (também com níveis mais elevados de certificação, como "certificações positivas" (0x13)) são organizadas em OpenPGP na Web of Trust.
+Nosso modelo de chave confirmação é um conceito muito mais simples para evitar problemas de usabilidade comuns relacionados com Web of Trust.
+Nós assumimos que as chaves são verificadas apenas a um determinado grau que ainda, é útil o suficiente para ser executado como "em movimento".
+Nós também não implementamos assinaturas (potencialmente transitivas) de confiança ou um de banco de dados de confiança proprietário como no GnuPG.
+Além disso, as teclas que contêm, pelo menos, um ID de utilizador certificado por uma chave de confiança, serão marcadas como "confirmado" na listagem de chaves. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-pt-rBR/help_changelog.md b/OpenKeychain/src/main/res/raw-pt-rBR/help_changelog.md
index 629931b2c..e9c28e801 100644
--- a/OpenKeychain/src/main/res/raw-pt-rBR/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/help_changelog.md
@@ -1,17 +1,17 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: coloque cada frase em sua própria linha, Transifex coloca cada linha em seu próprio campo de tradução!)
## 3.9
- * Detection and handling of text data
- * Performance improvements
- * UI improvements for Security Token handling
+ * Detecção e tratamento de dados de texto
+ * Melhorias de desempenho
+ * UI melhorias para o manuseio do Token de Segurança
## 3.8
- * Redesigned key editing
- * Choose remember time individually when entering passwords
- * Facebook key import
+ * Edição de chave reestruturada
+ * Escolher individualmente lembretes quando digitar senhas
+ * Importar chave para o Facebook
## 3.7
diff --git a/OpenKeychain/src/main/res/raw-pt-rBR/help_start.md b/OpenKeychain/src/main/res/raw-pt-rBR/help_start.md
index 4cc331942..43b9c8dc3 100644
--- a/OpenKeychain/src/main/res/raw-pt-rBR/help_start.md
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/help_start.md
@@ -1,16 +1,16 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: coloque cada frase em sua própria linha, Transifex coloca cada linha em seu próprio campo de tradução!)
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
+## Como faço para ativar o OpenKeychain no K-9 Mail?
+Para usar OpenKeychain com K-9 Mail, você terá seguir estes passos:
+ 1. Abra o K-9 Mail toque e segure para abrir um menu sob a conta que você deseja utilizar com o OpenKeychain.
+ 2. Selecione "Configurações de conta", desça o menu e clique em "Criptografia".
+ 3. Clique em "Provedor OpenPGP" e selecione o OpenKeychain na lista.
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+## Eu encontrei um bug no OpenKeychain!
+Por favor reporte o bug utilizando o [rastreador de questões do OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+## Contribua
+Se você deseja nos ajudar a desenvolver o OpenKeychain contribuindo com o codigo [Siga o nosso pequeno guia no Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
+## Traduções
+Ajude a traduzir o OpenKeychain! Todos podem participar em [OpenKeychain no Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ru/advanced.md b/OpenKeychain/src/main/res/raw-ru/advanced.md
index 54a694084..70dc69ca6 100644
--- a/OpenKeychain/src/main/res/raw-ru/advanced.md
+++ b/OpenKeychain/src/main/res/raw-ru/advanced.md
@@ -1,9 +1,9 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-Advanced screen allows you to
-* share key in non-recommended ways
-* edit identities
-* edit subkeys
-* examine certificates in detail
+Дополнительный экран позволяет
+* публиковать ключ нерекомендуемыми способами
+* редактировать идентификаторы
+* редактировать доп. ключи
+* детально изучить сертификаты
-Only proceed if you know what you are doing! \ No newline at end of file
+Продолжайте только если знаете, что делаете! \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ru/help_about.md b/OpenKeychain/src/main/res/raw-ru/help_about.md
index b6ab6ac17..8ad503613 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_about.md
+++ b/OpenKeychain/src/main/res/raw-ru/help_about.md
@@ -2,7 +2,7 @@
[https://www.openkeychain.org](https://www.openkeychain.org)
-[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android.
+[OpenKeychain](https://www.openkeychain.org) — это реализация OpenPGP для Android.
Лицензия: GPLv3+
@@ -63,8 +63,8 @@
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
* [OkHttp](https://square.github.io/okhttp/) (Apache License v2)
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (Лицензия MIT)
+ * [Snackbar](https://github.com/nispok/snackbar) (Лицензия MIT)
* [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
diff --git a/OpenKeychain/src/main/res/raw-ru/help_certification.md b/OpenKeychain/src/main/res/raw-ru/help_certification.md
index f4811892d..34c361ea3 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-ru/help_certification.md
@@ -21,8 +21,8 @@
Эта сертификация представляет собой ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) , описанной в стандарте:
"Издатель такой подписи (поручитель) никак не оговаривает, что провёл какую-то проверку ключа и его связь с лицом, чьё имя указано в сертификате."
-Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
-Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
+Традиционно, сертификации (также с более высоким уровнем сертификации, такие как "позитивные сертификации" (0x13)) организованы в OpenPGP's Web of Trust.
+Наша модель подтверждения ключа является гораздо более простой концепцией, чтобы избежать наиболее распространённых проблем использования, связанных с Web of Trust.
+Мы предполагаем, что ключи проверяются только до определённой степени, приемлемой для выполнения "на ходу".
+Мы также не осуществляем (потенциально транзитивные) доверенные подписи или базы данных доверенных владельцев как в GnuPG.
+Кроме того, ключи, которые содержат по меньшей мере один идентификатор пользователя, сертифицированный доверенным ключом, будут отмечаться как "подтверждённые" в списке ключей. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-sr/help_changelog.md b/OpenKeychain/src/main/res/raw-sr/help_changelog.md
index 0fd4c11e4..1661e5569 100644
--- a/OpenKeychain/src/main/res/raw-sr/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-sr/help_changelog.md
@@ -2,9 +2,9 @@
## 3.9
- * Detection and handling of text data
- * Performance improvements
- * UI improvements for Security Token handling
+ * Откривање и руковање текстуалним подацима
+ * Побољшање перформанси
+ * Побољшања УИ-ја за руковање безбедносним токеном
## 3.8
diff --git a/OpenKeychain/src/main/res/raw-zh-rTW/advanced.md b/OpenKeychain/src/main/res/raw-zh-rTW/advanced.md
index 54a694084..6fbf91c41 100644
--- a/OpenKeychain/src/main/res/raw-zh-rTW/advanced.md
+++ b/OpenKeychain/src/main/res/raw-zh-rTW/advanced.md
@@ -1,9 +1,9 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]:#(注意:請將每個翻譯的字詞放在它原來的行列上,Transifex會將它們放在原來位置上!)
-Advanced screen allows you to
-* share key in non-recommended ways
-* edit identities
-* edit subkeys
-* examine certificates in detail
+前置的屏幕讓你可以
+*通過這種方式分享密匙(不推薦)
+編輯身份
+*編輯子金匙
+*檢測細節的是否標準
-Only proceed if you know what you are doing! \ No newline at end of file
+繼續工作,如果你知道自己在做甚麼的話:! \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-zh/advanced.md b/OpenKeychain/src/main/res/raw-zh/advanced.md
index 0f9563fc7..0a1741ce2 100644
--- a/OpenKeychain/src/main/res/raw-zh/advanced.md
+++ b/OpenKeychain/src/main/res/raw-zh/advanced.md
@@ -2,8 +2,8 @@
您可以在高级选项界面进行以下操作
以其他方式分享密钥(不推荐)
-编辑身份
-编辑子密钥
-查看证书详情
+* 编辑身份
+* 编辑子密钥
+* 查看证书详情
在进行这些操作前,请确认您了解具体操作可能带来的危险后果! \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-zh/help_about.md b/OpenKeychain/src/main/res/raw-zh/help_about.md
index 17672c57f..a6ee1740d 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_about.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_about.md
@@ -19,7 +19,7 @@
* 'Thialfihar' (APG 开发者)
* Tim Bray
-##临时贡献者
+## 临时贡献者
* Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
diff --git a/OpenKeychain/src/main/res/raw-zh/help_certification.md b/OpenKeychain/src/main/res/raw-zh/help_certification.md
index ac3a20c27..d9f43875a 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_certification.md
@@ -14,9 +14,9 @@
<img src="status_signature_expired_cutout_24dp"/>
已过期:这个密钥不再有效。只有它的拥有者能扩展它的有效期。
<img src="status_signature_revoked_cutout_24dp"/>
-已作废:这个密钥不再有效。它已经被所有者声明为已作废。
+已吊销:这个密钥不再有效。它已经被所有者声明为已吊销状态。
-## 进一步说明
+## 高级信息
OpenKeychain 中的“密钥确认”行为,是根据 OpenPGP 标准规定,通过创建认证实现的。
这个认证是一个 [“一般认证(0x10)”](http://tools.ietf.org/html/rfc4880#section-5.2.1) ,标准中的描述是:
“对于密钥持有者与密钥所示身份之间的关联,认证签发者不对其可靠性做出任何表态。”
diff --git a/OpenKeychain/src/main/res/raw-zh/help_changelog.md b/OpenKeychain/src/main/res/raw-zh/help_changelog.md
index 9f91ebe5b..db352e24e 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_changelog.md
@@ -2,9 +2,9 @@
## 3.9
- * Detection and handling of text data
- * Performance improvements
- * UI improvements for Security Token handling
+ * 检测并处理文本数据
+ * 性能提升
+ * 提升处理安全令牌的 UI 界面
## 3.8
@@ -25,7 +25,7 @@
* 加密的备份
* 安全性修复基于外部安全性审查
* YubiKey NEO key 创建向导
- * 支持基本的内部MIME
+ * 支持基本的内部 MIME
* 自动同步密钥
* 试验性功能: 关联密钥到Github, Twitter账户
* 试验性功能: 通过短语确认密钥
@@ -36,7 +36,7 @@
## 3.5
* 密钥删除同时撤销
- * 不安全的加密方法被改进
+ * 改进了不安全的加密方法
* 修复: 第一次向导成功后OpenKeychain不关闭
* API: 第8版
@@ -52,7 +52,7 @@
* 新的解密屏幕
* 一次解密多个文件
- * 更好的YubiKey错误处理机制
+ * 更好的处理 YubiKey 错误
## 3.2
@@ -64,7 +64,7 @@
* 修复同步后丢失联系人
* 需要安卓4
* 重新设计密钥界面
- * 简化加密学的倾向,安全密码更好的选择
+ * 简化加密选项,更好的选择加密算法
* 分离签名,任意选择签名密钥
* 修复:密钥有效却被显示为作废或者到期的问题
* 不受理过期或者作废的子密钥签名
diff --git a/OpenKeychain/src/main/res/raw/help_faq.md b/OpenKeychain/src/main/res/raw/help_faq.md
index 78f2f8cbd..61cc7f7c5 100644
--- a/OpenKeychain/src/main/res/raw/help_faq.md
+++ b/OpenKeychain/src/main/res/raw/help_faq.md
@@ -8,7 +8,7 @@
backups are encrypted with Advanced Encryption Standard (AES) using
securely generated Backup Codes.
2. On your PC, execute ``gpg --decrypt backup_YYYY-MM-DD.pgp | gpg --import`` (replace ``backup_YYYY-MM-DD.pgp`` with your backup file)
- 3. Enter the full Backup Code with uppercase letters and dashes, e.g., "ABCDEF-GHIJKL-MNOPQR-STUVWX"
+ 3. Enter the full Backup Code with uppercase letters and dashes, e.g., "ABCD-EFGH-IJKL-MNOP-QRST-UVWX"
## What is the best way to transfer my own key to OpenKeychain?
diff --git a/OpenKeychain/src/main/res/values-cs/strings.xml b/OpenKeychain/src/main/res/values-cs/strings.xml
index 9ba8e1e36..78903caa2 100644
--- a/OpenKeychain/src/main/res/values-cs/strings.xml
+++ b/OpenKeychain/src/main/res/values-cs/strings.xml
@@ -253,21 +253,11 @@
<string name="choice_8hours">8 hodin</string>
<string name="choice_forever">navždy</string>
<string name="choice_select_cert">Vybrat klíč</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Otevřít...</string>
<string name="error">Chyba</string>
<string name="error_message">Chyba: %s</string>
<string name="theme_dark">Temný</string>
<string name="theme_light">Světlý</string>
- <!--key flags-->
- <string name="flag_certify">Certifikovat</string>
- <string name="flag_sign">Podepsat</string>
- <string name="flag_encrypt">Zašifrovat</string>
- <string name="flag_authenticate">Authentikovat</string>
<!--sentences-->
<string name="wrong_passphrase">Chybné heslo.</string>
<string name="no_filemanager_installed">Není nainstalován žádný compatibilní správce souborů.</string>
@@ -636,7 +626,6 @@
<item>Přesunout podklíč do bezpečnostního tokenu</item>
</string-array>
<string name="edit_key_new_subkey">nový podklíč</string>
- <string name="edit_key_select_flag">Prosím vyberte alespoň jeden příznak!</string>
<string name="edit_key_error_add_identity">Přidejte alespoň jednu identitu!</string>
<string name="edit_key_error_add_subkey">Přidejte alespoň jeden podklíč!</string>
<string name="edit_key_error_bad_security_token_algo">Algoritmus nepodporován bezpečnostním tokenem!</string>
diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml
index 18bc957ce..def181445 100644
--- a/OpenKeychain/src/main/res/values-de/strings.xml
+++ b/OpenKeychain/src/main/res/values-de/strings.xml
@@ -2,18 +2,19 @@
<resources>
<!--GENERAL: Please put all strings inside quotes as described in example 1 on
http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
- <string name="app_name">OpenKeychain</string>
+ <string name="app_name">OpenKeychaingn</string>
<!--title-->
<string name="title_encrypt_text">Verschlüsseln</string>
<string name="title_encrypt_files">Verschlüsseln</string>
<string name="title_decrypt">Entschlüsseln</string>
<string name="title_add_subkey">Unterschlüssel hinzufügen</string>
+ <string name="title_change_master_key">Hauptschlüssel ändern</string>
<string name="title_edit_key">Schlüssel bearbeiten</string>
<string name="title_linked_create">Eine Verknüpfte-Identität erzeugen</string>
<string name="title_preferences">Einstellungen</string>
<string name="title_api_registered_apps">Apps</string>
<string name="title_key_server_preference">OpenPGP-Schlüsselserver</string>
- <string name="title_cache_ttl_preference">Zwischenspeicherdauer für Passwörter</string>
+ <string name="title_cache_ttl_preference">Merkdauer für Passwörter</string>
<string name="title_change_passphrase">Passwort ändern</string>
<string name="title_share_fingerprint_with">Teile Fingerabdruck über…</string>
<string name="title_share_key">Teile Schlüssel über...</string>
@@ -24,7 +25,7 @@
<string name="title_import_keys">Schlüssel importieren</string>
<string name="title_export_key">Schlüssel sichern</string>
<string name="title_export_keys">Schlüssel sichern</string>
- <string name="title_key_not_found">Schlüssel nicht gefunden</string>
+ <string name="title_key_not_found">Schlüssel wurde nicht gefunden</string>
<string name="title_send_key">Auf Schlüsselserver hochladen</string>
<string name="title_backup">Schlüssel sichern</string>
<string name="title_certify_key">Schlüssel bestätigen</string>
@@ -35,9 +36,10 @@
<string name="title_advanced_key_info">Erweitert</string>
<string name="title_delete_secret_key">DEINEN Schlüssel \'%s\' löschen?</string>
<string name="title_manage_my_keys">Meine Schlüssel verwalten</string>
+ <string name="title_alert_strip">Diesen Unterschlüssel kürzen</string>
<!--section-->
<string name="section_user_ids">Identitäten</string>
- <string name="section_security_token">Smartcard</string>
+ <string name="section_security_token">Security-Token</string>
<string name="section_linked_system_contact">Verknüpfter Systemkontakt</string>
<string name="section_keybase_proofs">Keybase.io-Nachweise</string>
<string name="section_should_you_trust">Sollte ich diesem Schlüssel vertrauen?</string>
@@ -79,7 +81,7 @@
<string name="btn_back">Zurück</string>
<string name="btn_no">Nein</string>
<string name="btn_match">Fingerabdrücke stimmen überein</string>
- <string name="btn_match_phrases">Passphrasen stimmen überein</string>
+ <string name="btn_match_phrases">Phrasen stimmen überein</string>
<string name="btn_share_encrypted_signed">Text verschlüsseln/signieren und teilen</string>
<string name="btn_copy_encrypted_signed">Text verschlüsseln/signieren und kopieren</string>
<string name="btn_paste_encrypted_signed">Text verschlüsseln/signieren und einfügen</string>
@@ -89,7 +91,7 @@
<string name="btn_share_decrypted_text">Teilen</string>
<string name="btn_open_with">Öffnen mit…</string>
<string name="btn_copy_decrypted_text">In die Zwischenablage kopieren</string>
- <string name="btn_decrypt_clipboard">Aus Zwischenablage lesen</string>
+ <string name="btn_decrypt_clipboard">Aus der Zwischenablage lesen</string>
<string name="btn_decrypt_files">Datei auswählen</string>
<string name="btn_encrypt_files">Dateien verschlüsseln</string>
<string name="btn_encrypt_text">Text verschlüsseln</string>
@@ -113,14 +115,14 @@
<string name="menu_search">Suchen</string>
<string name="menu_nfc_preferences">NFC-Einstellungen</string>
<string name="menu_beam_preferences">Beam-Einstellungen</string>
- <string name="menu_encrypt_to">Verschlüsseln nach…</string>
+ <string name="menu_encrypt_to">Verschlüsseln an…</string>
<string name="menu_select_all">Alles auswählen</string>
<string name="menu_export_all_keys">Alle Schlüssel exportieren</string>
<string name="menu_update_all_keys">Alle Schlüssel aktualisieren</string>
<string name="menu_advanced">Erweiterte Einstellungen</string>
<string name="menu_certify_fingerprint">Mit Fingerabdruck bestätigen</string>
- <string name="menu_certify_fingerprint_phrases">Mit Phrases bestätigen</string>
- <string name="menu_share_log">Log teilen</string>
+ <string name="menu_certify_fingerprint_phrases">Über Phrasen bestätigen</string>
+ <string name="menu_share_log">Protokoll teilen</string>
<string name="menu_change_password">Passwort ändern</string>
<string name="menu_keyserver_add">Hinzufügen</string>
<!--label-->
@@ -135,11 +137,11 @@
<string name="label_passphrase_again">Passwort wiederholen</string>
<string name="label_show_passphrase">Passwort anzeigen</string>
<string name="label_algorithm">Algorithmus</string>
- <string name="label_ascii_armor">Datei: ASCII Armor</string>
- <string name="label_file_ascii_armor">Aktiviere ASCII Armor</string>
+ <string name="label_ascii_armor">Datei: ASCII-Armor</string>
+ <string name="label_file_ascii_armor">ASCII-Armor aktivieren</string>
<string name="label_write_version_header">Lass andere wissen dass du OpenKeychain nutzt</string>
<string name="label_write_version_header_summary">Fügt \'OpenKeychain v2.7\' zu OpenPGP-Signaturen, Daten und exportierten Schlüsseln hinzu</string>
- <string name="label_use_num_keypad_for_security_token_pin">Zifferntastatur für Sicherheits-PIN benutzen</string>
+ <string name="label_use_num_keypad_for_security_token_pin">Zifferntastatur für Sicherheits-PIN des Security-Tokens verwenden</string>
<string name="label_asymmetric_from">Signiere mit:</string>
<string name="label_to">Verschlüsseln an</string>
<string name="label_delete_after_encryption">Dateien nach Verschlüsselung löschen</string>
@@ -147,13 +149,14 @@
<string name="label_encryption_algorithm">Verschlüsselungsalgorithmus</string>
<string name="label_hash_algorithm">Hash-Algorithmus</string>
<string name="label_symmetric">Mit Passwort verschlüsseln</string>
- <string name="label_passphrase_cache_ttl">Zwischenspeicherdauer für Passwörter</string>
+ <string name="label_passphrase_cache_ttl">Auswahl für Merkdauer anpassen</string>
<string name="label_passphrase_cache_subs">Passwort pro Unterschlüssel merken</string>
<string name="label_message_compression">Textkomprimierung</string>
<string name="label_file_compression">Dateikomprimierung</string>
<string name="label_keyservers">OpenPGP-Schlüsselserver auswählen</string>
<string name="label_key_id">Schlüssel-ID</string>
<string name="label_key_created">Schlüssel erzeugt %s</string>
+ <string name="label_key_type">Typ</string>
<string name="label_creation">Erzeugungsdatum</string>
<string name="label_expiry">Ablaufdatum</string>
<string name="label_usage">Verwendungszweck</string>
@@ -167,14 +170,14 @@
<string name="label_fingerprint">Fingerabdruck</string>
<string name="expiry_date_dialog_title">Ablaufdatum festlegen</string>
<string name="label_keyservers_title">Schlüsselserver</string>
- <string name="label_keyserver_settings_hint">Ziehen zum Ändern der Sortierung, tippen zum Bearbeiten/Löschen</string>
+ <string name="label_keyserver_settings_hint">Ziehen um Sortierung zu ändern, berühren zum Bearbeiten/Löschen</string>
<string name="label_selected_keyserver_title">Ausgewählter Schlüsselserver</string>
<string name="label_preferred">bevorzugt</string>
<string name="label_enable_compression">Komprimierung aktivieren</string>
<string name="label_encrypt_filenames">Dateinamen verschlüsseln</string>
<string name="label_hidden_recipients">Empfänger verbergen</string>
<string name="label_verify_keyserver_connection">Verbindung testen</string>
- <string name="label_only_trusted_keyserver">Nur vertrauenswürdige Schlüsselserver</string>
+ <string name="label_only_trusted_keyserver">Erlaube nur vertrauenswürdige Schlüsselserver</string>
<string name="label_enter_keyserver_url">URL</string>
<string name="label_keyserver_dialog_delete">Schlüsselserver löschen</string>
<string name="label_theme">Design</string>
@@ -184,18 +187,19 @@
<string name="pref_keybase_summary">Schlüssel auf Keybase.io suchen</string>
<string name="pref_facebook">Facebook</string>
<string name="pref_facebook_summary">Schlüssel auf Facebook suchen</string>
- <string name="label_sync_settings_keyserver_title">Schlüssel automatisch aktualisieren</string>
+ <string name="label_sync_settings_keyserver_title">Automatische Schlüsselaktualisierung</string>
<string name="label_sync_settings_keyserver_summary_on">Alle drei Tage werden die Schlüssel vom bevorzugten Schlüsselserver aktualistiert</string>
<string name="label_sync_settings_keyserver_summary_off">Schlüssel werden nicht automatisch aktualisiert</string>
+ <string name="label_sync_settings_wifi_title">Synchronisation nur bei WLAN</string>
<string name="label_sync_settings_contacts_title">Schlüssel mit Kontakten verknüpfen</string>
<string name="label_sync_settings_contacts_summary_on">Schlüssel basierend auf Namen und E-Mail-Adressen mit Kontakten verknüpfen. Das alles findet komplett offline auf deinem Gerät statt.</string>
<string name="label_sync_settings_contacts_summary_off">Neue Schlüssel werden nicht mit Kontakten verknüpft</string>
<!--label shown in Android settings under the OpenKeychain account-->
- <string name="keyserver_sync_settings_title">Schlüssel automatisch aktualisieren</string>
+ <string name="keyserver_sync_settings_title">Automatische Schlüsselaktualisierung</string>
<string name="label_experimental_settings_desc_title">Warnung</string>
- <string name="label_experimental_settings_desc_summary">Diese Funktionen sind noch nicht final oder das Ergebnis von Benutzererfahrungs-/Sicherheitsuntersuchungen. Verlasse dich daher nicht auf deren Sicherheit und melde uns bitte keine Probleme die dir begegnen!</string>
- <string name="label_experimental_settings_word_confirm_title">Bestätigung mit Phrases</string>
- <string name="label_experimental_settings_word_confirm_summary">Mit Phrases statt hexadezimalen Fingerabdrücken bestätigen</string>
+ <string name="label_experimental_settings_desc_summary">Diese Funktionen sind noch nicht final oder das Ergebnis von Benutzererfahrungs-/Sicherheitsuntersuchungen. Verlasse dich daher nicht auf deren Sicherheit und melde uns bitte keine auftretenden Probleme!</string>
+ <string name="label_experimental_settings_word_confirm_title">Bestätigung mit Phrasen</string>
+ <string name="label_experimental_settings_word_confirm_summary">Schlüssel über Phrasen anstelle hexadezimaler Fingerabdrücke bestätigen</string>
<string name="label_experimental_settings_linked_identities_title">Verknüpfte-Identitäten</string>
<string name="label_experimental_settings_linked_identities_summary">Schlüssel mit Twitter, GitHub, Webseiten oder DNS verknüpfen (ähnlich wie bei Keybase.io, aber dezentralisiert)</string>
<string name="label_experimental_settings_keybase_title">Keybase.io-Nachweise</string>
@@ -254,21 +258,26 @@
<string name="choice_8hours">8 Stunden</string>
<string name="choice_forever">für immer</string>
<string name="choice_select_cert">Einen Schlüssel auswählen</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Öffnen...</string>
+ <string name="rsa_2048">RSA 2048</string>
+ <string name="rsa_2048_description_html">kleinere Dateigröße, bis 2030 als sicher erachtet</string>
+ <string name="rsa_3072">RSA 3072</string>
+ <string name="rsa_3072_description_html">empfohlen, bis 2040 als sicher erachtet</string>
+ <string name="rsa_4096">RSA 4096</string>
+ <string name="rsa_4096_description_html">größere Dateigröße, bis nach 2040 als sicher erachtet</string>
+ <string name="ecc_p256">ECC P-256</string>
+ <string name="ecc_p256_description_html">sehr kleine Dateigröße, bis 2040 als sicher erachtet &lt;br/&gt; &lt;u&gt;experimentell, nicht von allen Implementierungen unterstützt&lt;/u&gt;</string>
+ <string name="ecc_p521">ECC P-521</string>
+ <string name="ecc_p521_description_html">kleine Dateigröße, bis nach 2040 als sicher erachtet &lt;br/&gt; &lt;u&gt;experimentell, nicht von allen Implementierungen unterstützt&lt;/u&gt;</string>
+ <string name="usage_none">Keine (nur Unterschlüssel-Bindung)</string>
+ <string name="usage_sign">Signieren</string>
+ <string name="usage_encrypt">Verschlüsseln</string>
+ <string name="usage_sign_and_encrypt">Signieren &amp; verschlüsseln</string>
<string name="error">Fehler</string>
<string name="error_message">Fehler: %s</string>
<string name="theme_dark">Dunkel</string>
<string name="theme_light">Hell</string>
- <!--key flags-->
- <string name="flag_certify">Beglaubigen</string>
- <string name="flag_sign">Signieren</string>
- <string name="flag_encrypt">Verschlüsseln</string>
- <string name="flag_authenticate">Authentifizieren</string>
+ <string name="strip">Kürzen</string>
<!--sentences-->
<string name="wrong_passphrase">Falsches Passwort.</string>
<string name="no_filemanager_installed">Kein passender Dateimanager installiert.</string>
@@ -278,11 +287,11 @@
<string name="passphrase_for_backup">Backupcode eingeben</string>
<string name="passphrase_for">Passwort für \'%s\' eingeben</string>
<string name="pin_for">PIN für \'%s\' eingeben</string>
- <string name="security_token_pin_for">PIN für Zugriff auf Smartcard für \'%s\' eingeben</string>
- <string name="security_token_nfc_text">Halte die Smartcard an den NFC-Marker an der Rückseite Deines Geräts.</string>
- <string name="security_token_nfc_wait">Halte die Smartcard an die Rückseite!</string>
- <string name="security_token_nfc_finished">Entferne jetzt die Smartcard.</string>
- <string name="security_token_nfc_try_again_text">Entferne jetzt die Smartcard und tippe \"Erneut versuchen\"</string>
+ <string name="security_token_pin_for">PIN für Zugriff auf Security-Token für \'%s\' eingeben</string>
+ <string name="security_token_nfc_text">Halte den Security-Token an den NFC-Marker auf der Rückseite deines Geräts.</string>
+ <string name="security_token_nfc_wait">Halte den Security-Token an die Rückseite!</string>
+ <string name="security_token_nfc_finished">Entferne jetzt den Security-Token.</string>
+ <string name="security_token_nfc_try_again_text">Entferne jetzt den Security-Token und berühre \"Erneut versuchen\".</string>
<string name="file_delete_confirmation_title">Originaldateien löschen?</string>
<string name="file_delete_confirmation">Die folgenden Dateien werden gelöscht: %s</string>
<string name="file_delete_successful">%1$d von %2$d Dateien wurden gelöscht.%3$s</string>
@@ -302,6 +311,7 @@
<string name="public_key_deletetion_confirmation">Schlüssel \'%s\' löschen?</string>
<string name="also_export_secret_keys">Exportiere auch private Schlüssel</string>
<string name="reinstall_openkeychain">Es ist ein bekannter Fehler im Zusammenhang mit Android aufgetreten. Bitte installiere OpenKeychain erneut, wenn du deine Kontakte mit Schlüsseln verknüpfen willst.</string>
+ <string name="alert_strip">Das Kürzen dieses Unterschlüssels macht ihn auf diesem Gerät unbrauchbar!</string>
<string name="key_exported">1 Schlüssel erfolgreich exportiert.</string>
<string name="keys_exported">%d Schlüssel erfolgreich exportiert.</string>
<string name="no_keys_exported">Keine Schlüssel exportiert.</string>
@@ -317,15 +327,16 @@
<string name="fingerprint_copied_to_clipboard">Fingerabdruck wurde in die Zwischenablage kopiert!</string>
<string name="select_key_to_certify">Bitte wähle einen Schlüssel aus, der für die Bestätigung genutzt werden soll!</string>
<string name="text_copied_to_clipboard">Text wurde in die Zwischenablage kopiert!</string>
- <string name="how_to_import">Wie importiere ich das auf meinem Desktop-PC?</string>
+ <string name="how_to_import">Wie kann ich das auf meinem Desktop-PC importieren?</string>
<!--errors
no punctuation, all lowercase,
they will be put after "error_message", e.g. "Error: file not found"-->
<string name="error_file_delete_failed">wurden nicht gelöscht, bitte manuell löschen!</string>
<string name="error_file_added_already">%s wurde bereits hinzugefügt.</string>
- <string name="error_file_not_found">Datei nicht gefunden</string>
+ <string name="error_file_not_found">Datei wurde nicht gefunden</string>
+ <string name="error_bad_data">Fehlerhafte Daten!</string>
<string name="error_no_secret_key_found">kein geeigneter privater Schlüssel gefunden</string>
- <string name="error_external_storage_not_ready">Externes Laufwerk ist nicht bereit</string>
+ <string name="error_external_storage_not_ready">Externer Speicher ist nicht bereit</string>
<string name="error_key_size_minimum512bit">Schlüssellänge muss mindestens 512 Bit betragen</string>
<string name="error_unknown_algorithm_choice">Unbekannter Algorithmus ausgewählt </string>
<string name="error_user_id_no_email">keine E-Mail-Adresse gefunden</string>
@@ -344,7 +355,7 @@
<string name="error_nothing_import_selected">Keine Schlüssel für den Import ausgewählt!</string>
<string name="error_contacts_key_id_missing">Abrufen der Schlüsselkennung von den Kontakten ist fehlgeschlagen!</string>
<string name="error_generic_report_bug">Ein allgemeiner Fehler trat auf, bitte reiche einen Fehlerbericht ein!</string>
- <string name="error_denied_storage_permission">Kann Dateien aus dem Speicher nicht lesen weil der Zugriff verweigert wurde!</string>
+ <string name="error_denied_storage_permission">Aus dem Speicher können keine Dateien gelesen werden, da der Zugriff verweigert wurde!</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Nicht Signiert</string>
<string name="decrypt_result_invalid_signature">Ungültige Signatur!</string>
@@ -370,7 +381,7 @@
<string name="progress_cancelling">Wird abgebrochen...</string>
<string name="progress_saving">Wird gespeichert…</string>
<string name="progress_importing">Wird importiert…</string>
- <string name="progress_benchmarking">Führe Benchmark-Test aus</string>
+ <string name="progress_benchmarking">Leistungstest wird ausgeführt...</string>
<string name="progress_revoking_uploading">Schlüssel wird widerrufen und hochgeladen..</string>
<string name="progress_updating">Schlüssel werden aktualisiert...</string>
<string name="progress_exporting">Wird exportiert…</string>
@@ -422,7 +433,7 @@
<string name="progress_verifying_keyserver_connection">Prüfe Verbindung</string>
<string name="progress_starting_orbot">Orbot wird gestartet…</string>
<!--action strings-->
- <string name="hint_cloud_search_hint">Via Name, E-Mail suchen...</string>
+ <string name="hint_cloud_search_hint">Über Name, E-Mail suchen...</string>
<!--key bit length selections-->
<string name="key_size_2048">2048</string>
<string name="key_size_3072">3072</string>
@@ -672,15 +683,15 @@
<item>Ablaufdatum ändern</item>
<item>Unterschlüssel widerrufen</item>
<item>Unterschlüssel kürzen</item>
- <item>Unterschlüssel auf YubiKey / Smartcard verschieben</item>
+ <item>Unterschlüssel auf Security-Token verschieben</item>
</string-array>
<string name="edit_key_new_subkey">neuer Unterschlüssel</string>
- <string name="edit_key_select_flag">Bitte mindestens ein Attribut wählen!</string>
+ <string name="edit_key_select_usage">Bitte Schlüsselgebrauch auswählen!</string>
<string name="edit_key_error_add_identity">Füge mindestens eine Identität hinzu!</string>
<string name="edit_key_error_add_subkey">Füge mindestens einen Unterschlüssel hinzu!</string>
- <string name="edit_key_error_bad_security_token_algo">Algorithmus von Smartcard nicht unterstützt</string>
- <string name="edit_key_error_bad_security_token_size">Schlüssellänge wird von dieser Smartcard nicht unterstützt!</string>
- <string name="edit_key_error_bad_security_token_stripped">Kann Schlüssel nicht auf Smartcard verschieben (entweder gekürzt oder \'auf Karte umleiten\')!</string>
+ <string name="edit_key_error_bad_security_token_algo">Algorithmus wird vom Security-Token nicht unterstützt!</string>
+ <string name="edit_key_error_bad_security_token_size">Schlüssellänge wird von diesem Security-Token nicht unterstützt!</string>
+ <string name="edit_key_error_bad_security_token_stripped">Schlüssel kann nicht auf Security-Token verschoben werden (entweder gekürzt oder bereits \'auf Security-Token umgeleitet\')!</string>
<!--Create key-->
<string name="create_key_upload">Mit dem Internet synchronisieren</string>
<string name="create_key_empty">Dieses Feld wird benötigt</string>
@@ -709,7 +720,7 @@
<!--View key-->
<string name="view_key_revoked">Widerrufen: Schlüssel darf nicht mehr genutzt werden!</string>
<string name="view_key_expired">Abgelaufen: Der Kontakt muss die Gültigkeit des Schlüssels verlängern!</string>
- <string name="view_key_expired_secret">Abgelaufen: Du kannst die Gültigkeit des Schlüssels verlängern, indem du ihn bearbeitest.</string>
+ <string name="view_key_expired_secret">Abgelaufen: Du kannst die Gültigkeit des Schlüssels verlängern, indem du ihn bearbeitest!</string>
<string name="view_key_my_key">Mein Schlüssel</string>
<string name="view_key_verified">Bestätigter Schlüssel</string>
<string name="view_key_unverified">Unbestätigt: QR-Code einscannen, um den Schlüssel zu bestätigen!</string>
@@ -741,7 +752,7 @@
<string name="cert_positive">positiv</string>
<string name="cert_revoke">widerrufen</string>
<string name="cert_verify_ok">OK</string>
- <string name="cert_verify_failed">fehlgeschlagen!</string>
+ <string name="cert_verify_failed">Fehlschlag!</string>
<string name="cert_verify_error">Fehler!</string>
<string name="cert_verify_unavailable">Schlüssel nicht verfügbar</string>
<!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
@@ -751,7 +762,7 @@
<string name="msg_ip_apply_batch">Stapel-Einfügeoperationen werden angewendet.</string>
<string name="msg_ip_bad_type_secret">Es wurde versucht einen privaten Schlüsselbund als Öffentlichen zu importieren. Dies ist ein Fehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_ip_delete_old_fail">Kein alter Schlüssel gelöscht (Einen neuen erzeugen?)</string>
- <string name="msg_ip_delete_old_ok">Alte Schlüssel aus der Datenbank löschen</string>
+ <string name="msg_ip_delete_old_ok">Alter Schlüssel wurde aus der Datenbank gelöscht</string>
<string name="msg_ip_encode_fail">Vorgang aufgrund eines Kodierungsfehlers fehlgeschlagen</string>
<string name="msg_ip_error_io_exc">Vorgang aufgrund eines Ein-/Ausgabefehlers fehlgeschlagen</string>
<string name="msg_ip_error_op_exc">Vorgang aufgrund eines Datenbankfehlers fehlgeschlagen</string>
@@ -830,9 +841,9 @@
<string name="msg_ip_uat_processing_unknown">Unbekanntes Benutzerattribut wird verarbeitet</string>
<string name="msg_ip_uat_cert_bad">Fehlerhafte Beglaubigung gefunden!</string>
<string name="msg_ip_uat_cert_error">Fehler beim Verarbeiten der Beglaubigung!</string>
- <string name="msg_ip_uat_cert_nonrevoke">Nicht widerrufbare Beglaubigung bereits vorhanden, überspringe.</string>
- <string name="msg_ip_uat_cert_old">Beglaubgigung ist älter als Vorherige, überspringe.</string>
- <string name="msg_ip_uat_cert_new">Beglaubigung ist aktueller, ersetze Vorherhige.</string>
+ <string name="msg_ip_uat_cert_nonrevoke">Nicht widerrufbare Beglaubigung bereits vorhanden, wird übersprungen.</string>
+ <string name="msg_ip_uat_cert_old">Beglaubgigung ist älter als Vorherige, wird übersprungen.</string>
+ <string name="msg_ip_uat_cert_new">Beglaubigung ist aktueller, Vorherhige wird ersetzt.</string>
<string name="msg_ip_uat_cert_good">Korrekte Beglaubigung von %1$s gefunden</string>
<string name="msg_ip_uat_cert_good_revoke">Korrekten Beglaubigungwiderruf von %1$s gefunden</string>
<plurals name="msg_ip_uat_certs_unknown">
@@ -847,7 +858,7 @@
<string name="msg_is">Importiere privaten Schlüssel %s</string>
<string name="msg_is_db_exception">Datenbankfehler!</string>
<string name="msg_is_importing_subkeys">Private Unterschlüssel werden verarbeitet</string>
- <string name="msg_is_error_io_exc">Fehler bei Kordierung des Schlüsselbunds</string>
+ <string name="msg_is_error_io_exc">Fehler beim Kodieren des Schlüsselbundes</string>
<string name="msg_is_merge_public">Importierte Daten werden in vorhandenen öffentlichen Schlüsselbund eingefügt</string>
<string name="msg_is_merge_secret">Importierte Daten werden in vorhandenen privaten Schlüsselbund eingefügt</string>
<string name="msg_is_merge_special">Eigenbeglaubigungsdaten aus öffentlichem Schlüsselbund werden eingefügt</string>
@@ -856,8 +867,8 @@
<string name="msg_is_subkey_ok">Privater Unterschlüssel %s als verfügbar markiert</string>
<string name="msg_is_subkey_empty">Privater Unterschlüssel %s als verfügbar, mit leerem Passwort, markiert</string>
<string name="msg_is_subkey_pin">Privater Unterschlüssel %s als verfügbar markiert, mit PIN</string>
- <string name="msg_is_subkey_stripped">Privater Unterschlüssel %s als gekürzt markiert</string>
- <string name="msg_is_subkey_divert">Privater Unterschlüssel %s als \'auf Karte umgeleitet\' markiert</string>
+ <string name="msg_is_subkey_stripped">Privater Unterschlüssel %s wurde als gekürzt markiert</string>
+ <string name="msg_is_subkey_divert">Privater Unterschlüssel %s als \'auf Security-Token umgeleitet\' markiert</string>
<string name="msg_is_success_identical">Schlüsselbund enthält keine neuen Daten, es gibt nichts zu tun</string>
<string name="msg_is_success">Privater Schlüsselbund erfolgreich importiert</string>
<!--Keyring Canonicalization log entries-->
@@ -878,21 +889,21 @@
<string name="msg_kc_revoke_dup">Entferne redundantes Schlüsselbund-Widerrufszertifikat</string>
<string name="msg_kc_notation_dup">Entferne redundante Vermerk-Beglaubigung</string>
<string name="msg_kc_notation_empty">Entferne leere Vermerk-Beglaubigung</string>
- <string name="msg_kc_sub">Verarbeite Unterschlüssel %s</string>
- <string name="msg_kc_sub_bad">Entferne ungültige Unterschlüssel-Zwischenbeglaubigung</string>
- <string name="msg_kc_sub_bad_err">Entferne fehlerhafte Unterschlüssel-Zwischenbeglaubigung</string>
- <string name="msg_kc_sub_bad_local">Zwischenbeglaubigung des Unterschlüssels mit \"Lokal\"-Attribut wird entfernt</string>
+ <string name="msg_kc_sub">Unterschlüssel %s werden verarbeitet</string>
+ <string name="msg_kc_sub_bad">Ungültige Unterschlüssel-Zwischenbeglaubigung wird entfernt</string>
+ <string name="msg_kc_sub_bad_err">Fehlerhafte Unterschlüssel-Zwischenbeglaubigung wird entfernt</string>
+ <string name="msg_kc_sub_bad_local">Unterschlüssel-Zwischenbeglaubigung mit \"Lokal\"-Attribut wird entfernt</string>
<string name="msg_kc_sub_bad_keyid">Unterschlüssel-Zwischenausstellerkennung stimmt nicht überein</string>
- <string name="msg_kc_sub_bad_time">Entferne Unterschlüssel-Zwischenbeglaubigung mit zukünftigem Zeitstempel</string>
+ <string name="msg_kc_sub_bad_time">Unterschlüssel-Zwischenbeglaubigung mit zukünftigem Zeitstempel wird entfernt</string>
<string name="msg_kc_sub_bad_time_early">Die Unterschlüssel-Zwischenbeglaubigung hat einen früheren Zeitstempel als sein Hauptschlüssel!</string>
<string name="msg_kc_sub_bad_type">Unbekannte Unterschlüsselbeglaubigungsart: %s</string>
- <string name="msg_kc_sub_dup">Entferne redundate Unterschlüssel-Zwischenbeglaubigung</string>
- <string name="msg_kc_sub_primary_bad">Entferne Unterschlüssel-Zwischenbeglaubigung aufgrund ungültiger primärer Zwischenbeglaubigung</string>
- <string name="msg_kc_sub_primary_bad_err">Entferne Unterschlüssel-Zwischenbeglaubigung aufgrund fehlerhafter primärer Zwischenbeglaubigung</string>
- <string name="msg_kc_sub_primary_none">Entferne Unterschlüssel-Zwischenbeglaubigung aufgrund fehlender primärer Zwischenbeglaubigung</string>
+ <string name="msg_kc_sub_dup">Redundate Unterschlüssel-Zwischenbeglaubigung wird entfernt</string>
+ <string name="msg_kc_sub_primary_bad">Unterschlüssel-Zwischenbeglaubigung wird aufgrund einer ungültigen primären Zwischenbeglaubigung entfernt</string>
+ <string name="msg_kc_sub_primary_bad_err">Unterschlüssel-Zwischenbeglaubigung wird aufgrund einer fehlerhaften primären Zwischenbeglaubigung entfernt</string>
+ <string name="msg_kc_sub_primary_none">Unterschlüssel-Zwischenbeglaubigung wird aufgrund einer fehlenden primären Zwischenbeglaubigung entfernt</string>
<string name="msg_kc_sub_no_cert">Keine gültige Beglaubigung für %s gefunden, entferne vom Schlüsselbund</string>
- <string name="msg_kc_sub_revoke_bad_err">Entferne fehlerhaftes Unterschlüssel-Widerrufszertifikat</string>
- <string name="msg_kc_sub_revoke_bad">Entferne fehlerhaftes Unterschlüssel-Widerrufszertifikat</string>
+ <string name="msg_kc_sub_revoke_bad_err">Fehlerhaftes Unterschlüssel-Widerrufszertifikat wird entfernt</string>
+ <string name="msg_kc_sub_revoke_bad">Fehlerhaftes Unterschlüssel-Widerrufszertifikat wird entfernt</string>
<string name="msg_kc_sub_revoke_dup">Entferne redundantes Unterschlüssel-Widerrufszertifikat</string>
<string name="msg_kc_sub_unknown_algo">Unterschlüssel verwendet unbekannten Algorithmus, wird nicht importiert...</string>
<string name="msg_kc_sub_algo_bad_encrpyt">Der Unterschlüssel soll für die Verschlüsselung genutzt werden, der zu verwendende Algorithmus unterstützt dies jedoch nicht.</string>
@@ -919,15 +930,15 @@
<string name="msg_kc_uid_no_cert">Keine gültige Eigenbeglaubigung für User-ID \'%s\' gefunden, wird aus Schlüsselbund entfernt</string>
<string name="msg_kc_uid_remove">Ungültige User-ID \'%s\' wird entfernt</string>
<string name="msg_kc_uid_dup">Doppelte User-ID \'%s\' wird entfernt. Der Schlüsselbund enthielt zwei davon. Hieraus könnten fehlende Beglaubigungen resultieren!</string>
- <string name="msg_kc_uid_too_many">Benutzer-ID \"%s\" entfernt. Mehr als 100 Benutzer-IDs werden nicht importiert!</string>
- <string name="msg_kc_uid_warn_encoding">User-ID nicht als UTF-8 verifiziert!</string>
+ <string name="msg_kc_uid_too_many">User-ID \'%s\' wird entfernt. Mehr als 100 User-IDs werden nicht importiert!</string>
+ <string name="msg_kc_uid_warn_encoding">User-ID konnte nicht als UTF-8 verifiziert werden!</string>
<string name="msg_kc_uat_jpeg">JPEG-Benutzerattribut wird verarbeitet</string>
<string name="msg_kc_uat_unknown">Unbekanntes Benutzerattribut wird verarbeitet</string>
- <string name="msg_kc_uat_bad_err">Entferne fehlerhafte Eigenbeglaubigung für Benutzerattribut</string>
+ <string name="msg_kc_uat_bad_err">Fehlerhafte Eigenbeglaubigung für Benutzerattribut wird entfernt</string>
<string name="msg_kc_uat_bad_local">Benutzerattributsbeglaubigung mit \'Lokal\'-Attribut wird entfernt</string>
<string name="msg_kc_uat_bad_time">Entferne Benutzerattribut mit zukünftigem Zeitstempel</string>
- <string name="msg_kc_uat_bad_type">Entferne Benutzerattributsbeglaubigung unbekannter Art (%s)</string>
- <string name="msg_kc_uat_bad">Entferne fehlerhafte Eigenbeglaubigung für Benutzerattribut</string>
+ <string name="msg_kc_uat_bad_type">Benutzerattributsbeglaubigung unbekannter Art wird entfernt (%s)</string>
+ <string name="msg_kc_uat_bad">Fehlerhafte Eigenbeglaubigung für Benutzerattribut wird entfernt</string>
<string name="msg_kc_uat_cert_dup">Entferne abgelaufene Eigenbeglaubigung für Benutzerattribut</string>
<string name="msg_kc_uat_dup">Doppeltes Benutzerattribut wird entfernt. Der Schlüsselbund enthielt zwei davon. Hieraus könnten fehlende Beglaubigungen resultieren!</string>
<string name="msg_kc_uat_foreign">Entferne fremde Benutzerattributsbeglaubigung von</string>
@@ -935,27 +946,26 @@
<string name="msg_kc_uat_revoke_old">Entferne abgelaufenes Widerrufszertifikat der Benutzerattribute</string>
<string name="msg_kc_uat_no_cert">Keine gültige Eigenbeglaubigung für das Benutzerattribut gefunden, wird aus Schlüsselbund entfernt</string>
<string name="msg_kc_uat_remove">Ungültiges Benutzerattribut wird entfernt</string>
- <string name="msg_kc_uat_warn_encoding">User-ID nicht als UTF-8 verifiziert!</string>
+ <string name="msg_kc_uat_warn_encoding">User-ID konnte nicht als UTF-8 verifiziert werden!</string>
<!--Keyring merging log entries-->
- <string name="msg_mg_error_secret_dummy">Neuer öffentlicher Unterschlüssel gefunden, aber Erzeugung von privaten Unterschlüsseldummys wird nicht unterstützt!</string>
- <string name="msg_mg_error_heterogeneous">Versuch Schlüsselbünde mit unterschiedlichen Fingerabdrücken zusammenzuführen!</string>
- <string name="msg_mg_error_encode">Schwerer Fehler bei Kodierung der Signatur!</string>
- <string name="msg_mg_public">Wird in öffentlichen Schlüsselbund %s eingefügt
-</string>
- <string name="msg_mg_secret">In privaten Schlüsselbund %s einfügen</string>
+ <string name="msg_mg_error_secret_dummy">Neuer öffentlicher Unterschlüssel wurde gefunden, Erzeugung von privaten Unterschlüsseldummys wird aber nicht unterstützt!</string>
+ <string name="msg_mg_error_heterogeneous">Es wurde versucht Schlüsselbünde mit unterschiedlichen Fingerabdrücken zusammenzuführen!</string>
+ <string name="msg_mg_error_encode">Schwerer Fehler beim Kodieren der Signatur!</string>
+ <string name="msg_mg_public">Wird in öffentlichen Schlüsselbund eingefügt %s</string>
+ <string name="msg_mg_secret">Wird in privaten Schlüsselbund eingefügt %s</string>
<string name="msg_mg_new_subkey">Neuer Unterschlüssel %s wird hinzugefügt</string>
<string name="msg_mg_found_new">%s neue Beglaubigungen in Schlüsselbund gefunden</string>
- <string name="msg_mg_unchanged">Nichts zusammenzuführen</string>
+ <string name="msg_mg_unchanged">Nichts einzufügen</string>
<!--createSecretKeyRing-->
<string name="msg_cr">Neuer Hauptschlüssel wird erzeugt</string>
<string name="msg_cr_error_no_master">Keine Hauptschlüsseloptionen spezifiziert!</string>
<string name="msg_cr_error_no_user_id">Schlüsselbünde müssen mindestens eine User-ID enthalten!</string>
<string name="msg_cr_error_no_certify">Hauptschlüssel benötigt das Attribut beglaubigen!</string>
<string name="msg_cr_error_null_expiry">Ablaufdatum kann bei Schlüsselerzeugung nicht \"identisch wie vorher\" sein. Dies ist ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_cr_error_keysize_2048">Schlüsselgröße muss größer/gleich 2048 sein!</string>
+ <string name="msg_cr_error_keysize_2048">Schlüssellänge muss größer-gleich 2048 sein!</string>
<string name="msg_cr_error_no_curve">Keine Schlüssellänge spezifiziert! Dies ist ein Progammierfehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_cr_error_no_keysize">Keine Elliptische Kurve spezifiziert! Dies ist ein Progammierfehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_cr_error_internal_pgp">Interner OpenPGP Fehler!</string>
+ <string name="msg_cr_error_internal_pgp">Interner OpenPGP-Fehler!</string>
<string name="msg_cr_error_unknown_algo">Unbekannter Algorithmus ausgewählt. Dies ist ein Progammierfehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_cr_error_flags_dsa">Falsche Schlüsselattribute ausgewählt, DSA kann nicht zum Verschlüsseln verwendet werden!</string>
<string name="msg_cr_error_flags_elgamal">Falsche Schlüsselattribute ausgewählt, ElGamal kann nicht zum Signieren verwendet werden!</string>
@@ -963,50 +973,50 @@
<string name="msg_cr_error_flags_ecdh">Falsche Schlüsselattribute ausgewählt, ECDH kann nicht zum Signieren verwendet werden!</string>
<!--modifySecretKeyRing-->
<string name="msg_mr">Schlüsselbund %s wird verändert</string>
- <string name="msg_mf_divert">Leite für Verschlüsselungsoperationen auf Smartcard um</string>
- <string name="msg_mf_error_divert_newsub">Erzeugung neuer Unterschlüssel wird für \'auf Karte umgeleitete\' Hauptschlüssel nicht unterstützt!</string>
- <string name="msg_mf_error_divert_serial">Die Seriennummer eines \'auf Karte umgeleiteten\' Schlüssels muss 16 Byte lang sein! Dies ist ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_mf_divert">Kryptographische Vorgänge werden auf Security-Token umgeleitet</string>
+ <string name="msg_mf_error_divert_newsub">Die Erzeugung neuer Unterschlüssel wird für \'auf Security-Token umgeleitete\' Hauptschlüssel nicht unterstützt!</string>
+ <string name="msg_mf_error_divert_serial">Die Seriennummer eines \'auf Security-Token umgeleiteten\' Schlüssels muss 16 Byte lang sein! Dies ist ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_mf_error_encode">Kodierungsfehler!</string>
<string name="msg_mf_error_fingerprint">Tatsächlicher Fingerabdruck des Schlüssels entspricht nicht dem Erwarteten!</string>
- <string name="msg_mf_error_keyid">Keine Schlüssel-ID. Dies ist ein interner Fehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_mf_error_keyid">Keine Schlüssel-ID gefunden. Dies ist ein interner Fehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_mf_error_integrity">Interner Fehler, Integritätsprüfung fehlgeschlagen!</string>
<string name="msg_mf_error_master_none">Keine Hauptbeglaubigung zum damit Arbeiten gefunden! (Alle widerrufen?)</string>
<string name="msg_mf_error_noexist_primary">Falsche primäre User-ID spezifiziert!</string>
<string name="msg_mf_error_noexist_revoke">Falsche User-ID für Widerruf spezifiziert!</string>
<string name="msg_mf_error_restricted">Versuch einen eingeschränkten Vorgang ohne Passwort auszuführen! Dies ist ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_mf_error_revoked_primary">Widerrufene User-IDs können nicht primäre IDs sein!</string>
+ <string name="msg_mf_error_revoked_primary">Widerrufene User-IDs können keine primären IDs sein!</string>
<string name="msg_mf_error_null_expiry">Ablaufdatum kann bei Unterschlüsselerzeugung nicht \"identisch wie vorher\" sein. Dies ist ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_mf_error_noop">Nichts zu tun!</string>
<string name="msg_mf_error_passphrase_master">Schwerer Fehler beim Entschlüsseln des Hauptschlüssels! Dies ist wahrscheinlich ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_mf_error_pgp">Interner OpenPGP Fehler!</string>
+ <string name="msg_mf_error_pgp">Interner OpenPGP-Fehler!</string>
<string name="msg_mf_error_sig">Signaturfehler!</string>
<string name="msg_mf_error_sub_stripped">Gekürzter Unterschlüssel %s kann nicht verändert werden!</string>
<string name="msg_mf_error_subkey_missing">Versuch mit fehlendem Unterschlüssel %s zu arbeiten!</string>
- <string name="msg_mf_error_conflicting_nfc_commands">Kann Schlüssel nicht auf Smartcard verschieben wenn im gleichen Vorgang die Kartensignatur erstellt wird.</string>
- <string name="msg_mf_error_duplicate_keytocard_for_slot">Smartcard unterstützt nur einen Slot pro Schlüsseltyp.</string>
- <string name="msg_mf_error_invalid_flags_for_keytocard">Ungeeignete Schlüsselattribute für Schlüssel auf Smartcard.</string>
+ <string name="msg_mf_error_conflicting_nfc_commands">Der Schlüssel kann nicht auf den Security-Token verschoben werden, wenn im selben Vorgang darauf eine Signatur erzeugt wird.</string>
+ <string name="msg_mf_error_duplicate_keytocard_for_slot">Der Security-Token unterstützt nur einen Slot pro Schlüsseltyp.</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">Ungeeignete Schlüsselattribute für Schlüssel des Security-Tokens.</string>
<string name="msg_mf_master">Hauptbeglaubigungen werden verändert</string>
<string name="msg_mf_notation_empty">Füge leeres Vermerk-Paket hinzu</string>
<string name="msg_mf_notation_pin">Füge PIN-Vermerk-Paket hinzu</string>
<string name="msg_mf_passphrase">Passwort für Schlüsselbund wird geändert</string>
- <string name="msg_mf_pin">PIN auf Karte wird geändert</string>
- <string name="msg_mf_admin_pin">Admin-PIN auf Karte wird geändert</string>
+ <string name="msg_mf_pin">PIN des Security-Tokens wird geändert</string>
+ <string name="msg_mf_admin_pin">Admin-PIN des Security-Tokens wird geändert</string>
<string name="msg_mf_passphrase_key">Erneute Verschlüsselung des Unterschlüssels %s mit neuem Passwort</string>
<string name="msg_mf_passphrase_empty_retry">Festlegen eines neuen Passworts fehlgeschlagen, erneuter Versuch mit leerem altem Passwort</string>
<string name="msg_mf_passphrase_fail">Passwort des Unterschlüssels konnte nicht geändert werden! (Hat er ein anderes Passwort als die anderen Schlüssel?)</string>
<string name="msg_mf_primary_replace_old">Beglaubigung von vorheriger primärer User-ID wird ersetzt</string>
- <string name="msg_mf_primary_new">Neue Beglaubigung für neue primäre User-ID wird erzeugt</string>
+ <string name="msg_mf_primary_new">Beglaubigung für neue primäre User-ID wird erzeugt</string>
<string name="msg_mf_restricted_mode">Wechsle zu eingeschränktem Vorgangsmodus</string>
<string name="msg_mf_subkey_change">Unterschlüssel %s wird verändert</string>
- <string name="msg_mf_require_divert">Werde für Verschlüsselungsoperationen auf Smartcard umleiten</string>
+ <string name="msg_mf_require_divert">Kryptographische Vorgänge werden auf Security-Token umgeleitet</string>
<string name="msg_mf_require_passphrase">Für die Vorgänge ist ein Passwort erforderlich</string>
- <string name="msg_mf_subkey_new">Füge neuen Unterschlüssel vom Typ %s hinzu</string>
+ <string name="msg_mf_subkey_new">Neuen Unterschlüssel vom Typ %s werden hinzugefügt</string>
<string name="msg_mf_subkey_new_id">Neue Unterschlüsselkennung: %s</string>
<string name="msg_mf_error_past_expiry">Ablaufdatum kann nicht in der Vergangenheit liegen!</string>
<string name="msg_mf_subkey_revoke">Unterschlüssel %s wird widerrufen</string>
- <string name="msg_mf_subkey_strip">Kürze Unterschlüssel %s</string>
- <string name="msg_mf_keytocard_start">Verschiebe Unterschlüssel %s auf Smartcard</string>
- <string name="msg_mf_keytocard_finish">%1$s auf Smartcard %2$s verschoben</string>
+ <string name="msg_mf_subkey_strip">Unterschlüssel %s wird gekürzt</string>
+ <string name="msg_mf_keytocard_start">Unterschlüssel %s wird auf den Security-Token umgeleitet</string>
+ <string name="msg_mf_keytocard_finish">%1$s wurde auf den Security-Token %2$s verschoben</string>
<string name="msg_mf_success">Schlüsselbund erfolgreich verändert</string>
<string name="msg_mf_uid_add">User-ID %s wird hinzugefügt</string>
<string name="msg_mf_uid_primary">Primäre User-ID wird geändert in %s</string>
@@ -1015,25 +1025,25 @@
<string name="msg_mf_uat_error_empty">Benutzerattribut darf nicht leer sein!</string>
<string name="msg_mf_uat_add_image">Bild-Benutzerattribut wird hinzugefügt</string>
<string name="msg_mf_uat_add_unknown">Unbekannter Benutzerattributstyp wird hinzugefügt</string>
- <string name="msg_mf_unlock_error">Fehler beim entsperren des Schlüsselbunds</string>
+ <string name="msg_mf_unlock_error">Fehler beim Entsperren des Schlüsselbundes!</string>
<string name="msg_mf_unlock">Schlüsselbund wird entsperrt</string>
<!--Consolidate-->
<string name="msg_con">Datenbank wird zusammengeführt</string>
<string name="msg_con_error_bad_state">Zusammenführung wurde gestartet, während keine Datenbank zwischengespeichert war! Dies ist wahrscheinlich ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_con_error_concurrent">Zusammenführung abgebrochen, läuft bereits in einem anderen Thread!</string>
+ <string name="msg_con_error_concurrent">Zusammenführung wurde abgebrochen, läuft bereits in einem anderen Thread!</string>
<string name="msg_con_save_secret">Private Schlüsselbünde werden gespeichert</string>
<string name="msg_con_save_public">Öffentliche Schlüsselbünde werden gespeichert</string>
<string name="msg_con_db_clear">Datenbank wird geleert</string>
- <string name="msg_con_success">Datenbank erfolgreich zusammengeführt!</string>
+ <string name="msg_con_success">Datenbank wurde erfolgreich zusammengeführt!</string>
<string name="msg_con_critical_in">Beginne kritische Phase!</string>
<string name="msg_con_critical_out">Verlasse kritische Phase</string>
<string name="msg_con_delete_public">Lösche Zwischenspeicherdatei des öffentlichen Schlüsselbundes</string>
<string name="msg_con_delete_secret">Lösche Zwischenspeicherdatei des privaten Schlüsselbundes</string>
- <string name="msg_con_error_db">Fehler bei der Öffnung der Datenbank!</string>
+ <string name="msg_con_error_db">Fehler beim Öffnen der Datenbank!</string>
<string name="msg_con_error_io_public">Ein-/Ausgabefehler beim Schreiben von öffentlichen Schlüsseln in den Zwischenspeicher!</string>
<string name="msg_con_error_io_secret">Ein-/Ausgabefehler beim Schreiben von privaten Schlüsseln in den Zwischenspeicher!</string>
<string name="msg_con_error_public">Fehler beim Reimportieren der öffentlichen Schlüssel!</string>
- <string name="msg_con_error_secret">Fehler beim reimportieren der privaten Schlüssel!</string>
+ <string name="msg_con_error_secret">Fehler beim Reimportieren der privaten Schlüssel!</string>
<string name="msg_con_recover">Zusammenführungsvorgang wird fortgesetzt</string>
<string name="msg_con_recursive">Rekursive Zusammenführung wird übersprungen</string>
<string name="msg_con_recover_unknown">Zusammenführungsvorgang aus unbekanntem Zustand wird fortgesetzt</string>
@@ -1053,21 +1063,21 @@
<string name="msg_ed">Schlüsselvorgang wird ausgeführt</string>
<string name="msg_ed_caching_new">Neues Passwort wird zwischengespeichert</string>
<string name="msg_ed_error_no_parcel">\"SaveKeyringParcel\" fehlt! (Dies ist ein Fehler, bitte reiche einen Fehlerbericht ein!)</string>
- <string name="msg_ed_error_key_not_found">Schlüssel nicht gefunden!</string>
+ <string name="msg_ed_error_key_not_found">Schlüssel wurde nicht gefunden!</string>
<string name="msg_ed_error_extract_public_upload">Fehler beim Extrahieren des öffentlichen Schlüssels fürs Hochladen!</string>
<string name="msg_ed_fetching">Zu bearbeitender Schlüssel wird abgerufen (%s)</string>
<string name="msg_ed_success">Schlüsselvorgang erfolgreich</string>
<!--Promote key-->
<string name="msg_pr">Öffentlicher Schlüssel zu privatem Schlüssel hochgestuft</string>
<string name="msg_pr_all">Stufe alle Unterschlüssel hoch</string>
- <string name="msg_pr_error_key_not_found">Schlüssel nicht gefunden!</string>
+ <string name="msg_pr_error_key_not_found">Schlüssel wurde nicht gefunden!</string>
<string name="msg_pr_fetching">Zu bearbeitender Schlüssel wird abgerufen (%s)</string>
<string name="msg_pr_subkey_match">Stufe Unterschlüssel hoch: %s</string>
- <string name="msg_pr_subkey_nomatch">Unterschlüssel nicht auf Smartcard: %s</string>
+ <string name="msg_pr_subkey_nomatch">Unterschlüssel ist nicht auf dem Security-Token: %s</string>
<string name="msg_pr_success">Schlüssel erfolgreich hochgestuft</string>
<!--Other messages used in OperationLogs-->
<string name="msg_ek_error_dummy">Schlüsselbund mit gekürztem Hauptschlüssel kann nicht bearbeitet werden!</string>
- <string name="msg_ek_error_not_found">Schlüssel nicht gefunden!</string>
+ <string name="msg_ek_error_not_found">Schlüssel wurde nicht gefunden!</string>
<!--Messages for DecryptVerify operation-->
<string name="msg_dc_askip_bad_flags">Schlüssel ist kein gültiger Verschlüsselung-Schlüssel, überspringe...</string>
<string name="msg_dc_askip_unavailable">Schlüssel nicht verfügbar, überspringe...</string>
@@ -1076,7 +1086,7 @@
<string name="msg_dc_asym">Block asymmetrisch verschlüsselter Daten für Schlüssel %s gefunden</string>
<string name="msg_dc_charset">Ein Zeichensatz-Header wurde gefunden: \'%s\'</string>
<string name="msg_dc_backup_version">Backupversionsheader gefunden: \'%s\'</string>
- <string name="msg_dc_clear_data">Verarbeite Klartextdaten</string>
+ <string name="msg_dc_clear_data">Klartextdaten werden verarbeitet</string>
<string name="msg_dc_clear_decompress">Komprimierte Daten werden entpackt</string>
<string name="msg_dc_clear_meta_file">Dateiname: %s</string>
<string name="msg_dc_clear_meta_mime">MIME-Typ: %s</string>
@@ -1086,19 +1096,19 @@
<string name="msg_dc_clear_signature_bad">Signaturprüfung NICHT in Ordnung!</string>
<string name="msg_dc_clear_signature_check">Signaturdaten werden verifiziert</string>
<string name="msg_dc_clear_signature_ok">Signaturprüfung in Ordnung</string>
- <string name="msg_dc_clear_signature">Speichere Signatur für später</string>
- <string name="msg_dc_clear">Verarbeite Klartextdaten</string>
+ <string name="msg_dc_clear_signature">Signaturdaten werden für später gespeichert</string>
+ <string name="msg_dc_clear">Klartextdaten werden verarbeitet</string>
<string name="msg_dc_error_bad_passphrase">Fehler beim Entsperren des Schlüssels, falsches Passwort!</string>
- <string name="msg_dc_error_sym_passphrase">Fehler beim Entschlüsseln der Daten, falsche Passphrase?</string>
+ <string name="msg_dc_error_sym_passphrase">Fehler beim Entschlüsseln der Daten, falsches Passwort?</string>
<string name="msg_dc_error_corrupt_data">Daten beschädigt!</string>
- <string name="msg_dc_error_extract_key">Unbekannter Fehler bei Schlüsselentsperrung!</string>
- <string name="msg_dc_error_integrity_check">Integritätsprüfungsfehler!</string>
- <string name="msg_dc_error_invalid_data">Kein gültiger OpenPGP-verschlüsselter oder -signierter Inhalt gefunden!</string>
- <string name="msg_dc_error_io">Beim Einlesen der Eingangsdaten ist ein Fehler aufgetreten!</string>
+ <string name="msg_dc_error_extract_key">Unbekannter Fehler beim Entsperren des Schlüssels!</string>
+ <string name="msg_dc_error_integrity_check">Integritätsprüfung fehlgeschlagen!</string>
+ <string name="msg_dc_error_invalid_data">Es wurde kein gültiger OpenPGP-verschlüsselter oder -signierter Inhalt gefunden!</string>
+ <string name="msg_dc_error_io">Beim Einlesen der Eingabedaten ist ein Fehler aufgetreten!</string>
<string name="msg_dc_error_input">Fehler beim Öffnen des eingehenden Datenstroms!</string>
<string name="msg_dc_error_no_data">Keine verschlüsselten Daten im Datenstrom gefunden!</string>
<string name="msg_dc_error_no_key">Keine verschlüsselten Daten mit bekanntem privatem Schlüssel im Datenstrom gefunden!</string>
- <string name="msg_dc_error_no_signature">Fehlende Signaturdaten!</string>
+ <string name="msg_dc_error_no_signature">Signaturdaten fehlen!</string>
<string name="msg_dc_error_pgp_exception">Während eines Vorgangs ist ein OpenPGP-Ausnahmefehler aufgetreten!</string>
<string name="msg_dc_integrity_check_ok">Integritätsprüfung in Ordnung!</string>
<string name="msg_dc_ok_meta_only">Es wurden nur Metadaten angefragt, überspringe Entschlüsselung</string>
@@ -1120,10 +1130,10 @@
<string name="msg_dc_insecure_mdc_missing">Modifikationserkennungscode-Paket (engl. MDC packet) fehlt! Das kann passieren wenn die Verschlüsselungsanwendung veraltet ist, oder durch einen Zurückstufungsangriff.</string>
<string name="msg_dc_insecure_key">Unsicherer Schlüssel: Entweder ist die Bitlänge von RSA/DSA/ElGamal zu kurz oder die ECC-Kurve bzw. der ECC-Algorithmus wird als unsicher angesehen! Das kann vorkommen wenn die Anwendung veraltet ist, oder durch einen Angriff.</string>
<!--Messages for VerifySignedLiteralData operation-->
- <string name="msg_vl">Starte Signaturprüfung</string>
+ <string name="msg_vl">Signaturprüfung wird gestartet</string>
<string name="msg_vl_error_no_siglist">Keine Signaturliste in signierten Literaldaten!</string>
- <string name="msg_vl_error_wrong_key">Nachricht nicht mit erwartetem Schlüssels signiert!</string>
- <string name="msg_vl_error_no_signature">Fehlende Signaturdaten!</string>
+ <string name="msg_vl_error_wrong_key">Nachricht wurde nicht mit dem erwarteten Schlüssel signiert!</string>
+ <string name="msg_vl_error_no_signature">Signaturdaten fehlen!</string>
<string name="msg_vl_error_missing_literal">Keine Nutzdaten in signierten Literaldaten</string>
<string name="msg_vl_clear_meta_file">Dateiname: %s</string>
<string name="msg_vl_clear_meta_mime">MIME-Typ: %s</string>
@@ -1133,14 +1143,14 @@
<string name="msg_vl_error_integrity_check">Integritätsprüfung fehlgeschlagen!</string>
<string name="msg_vl_ok">OK</string>
<!--Messages for SignEncrypt operation-->
- <string name="msg_se">Starte Signier-/Verschlüsselungsvorgang</string>
+ <string name="msg_se">Signier-/Verschlüsselungsvorgang wird gestartet</string>
<string name="msg_se_input_bytes">Eingabe aus Bytearray wird verarbeitet</string>
<string name="msg_se_input_uri">Eingabe aus URI wird verarbeitet</string>
<string name="msg_se_error_no_input">Keine Eingabe vorhanden!</string>
<string name="msg_se_error_input_uri_not_found">Fehler beim Öffnen der URI zum Lesen!</string>
<string name="msg_se_error_output_uri_not_found">Fehler beim Öffnen der URI zum Schreiben!</string>
<string name="msg_se_error_too_many_inputs">Mehr Eingaben als Ausgaben spezifiziert! Dies ist vermutlich ein Programmierfehler, diesen bitte melden!</string>
- <string name="msg_se_success">Signier-/Verschlüsselungsvorgang erfolgreich</string>
+ <string name="msg_se_success">Signier-/Verschlüsselungsvorgang war erfolgreich</string>
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_pse_asymmetric">Bereite öffentliche Schlüssel für Verschlüsselung vor</string>
<string name="msg_pse_compressing">Kompression wird vorbereitet</string>
@@ -1148,23 +1158,23 @@
<string name="msg_pse_error_bad_passphrase">Falsches Passwort!</string>
<string name="msg_pse_error_io">Während eines Vorgangs ist ein Ein-/Ausgabefehler aufgetreten!</string>
<string name="msg_pse_error_key_sign">Ausgewählter Signaturschlüssel kann keine Daten signieren!</string>
- <string name="msg_pse_error_sign_key">Fehler bei Abruf des Signaturschlüssels!</string>
+ <string name="msg_pse_error_sign_key">Fehler beim Abrufen des Signaturschlüssels!</string>
<string name="msg_pse_error_nfc">NFC-Datenfehler!</string>
<string name="msg_pse_error_no_passphrase">Kein Passwort angegeben!</string>
- <string name="msg_pse_error_pgp">Interner OpenPGP Fehler!</string>
- <string name="msg_pse_error_sig">Während eines Vorgang ist ein OpenPGP-Signatur-Ausnahmefehler aufgetreten!</string>
- <string name="msg_pse_error_unlock">Unbekannter Fehler bei Schlüsselentsperrung!</string>
+ <string name="msg_pse_error_pgp">Interner OpenPGP-Fehler!</string>
+ <string name="msg_pse_error_sig">Es ist ein OpenPGP-Signatur-Ausnahmefehler aufgetreten!</string>
+ <string name="msg_pse_error_unlock">Unbekannter Fehler beim Entsperren des Schlüssels!</string>
<string name="msg_pse_key_ok">Verschlüssele für Schlüssel: %s</string>
<string name="msg_pse_key_unknown">Fehlender Schlüssel für Verschlüsselung: %s</string>
<string name="msg_pse_key_warn">Fehlerhafter Schlüssel für Verschlüsselung: %s</string>
- <string name="msg_pse_ok">Signierungs-/Verschlüsselungsvorgang erfolgreich!</string>
+ <string name="msg_pse_ok">Signierungs-/Verschlüsselungsvorgang war erfolgreich!</string>
<string name="msg_pse_pending_nfc">NFC-Token wird benötigt, Benutzereingabe wird angefordert…</string>
<string name="msg_pse_pending_passphrase">Passwort erforderlich, Benutzereingabe wird angefordert…</string>
- <string name="msg_pse_signing">Signiere Daten (ohne Verschlüsselung)</string>
+ <string name="msg_pse_signing">Daten werden signiert (ohne Verschlüsselung)</string>
<string name="msg_pse_signing_cleartext">Klartextsignatur wird erzeugt</string>
<string name="msg_pse_signing_detached">Abgetrennte Signatur wird erzeugt</string>
<string name="msg_pse_sigcrypting">Verschlüssele Daten mit Signatur</string>
- <string name="msg_pse">Starte Signier- und/oder Verschlüsselungsvorgang</string>
+ <string name="msg_pse">Signier- und/oder Verschlüsselungsvorgang wird gestartet</string>
<string name="msg_pse_symmetric">Bereite symmetrische Verschlüsselung vor</string>
<string name="msg_crt_certifying">Beglaubigungen werden erzeugt</string>
<plurals name="msg_crt_certify_uids">
@@ -1176,7 +1186,7 @@
<item quantity="other">Beglaubige %1$d Benutzerattribute für Schlüssel %2$s</item>
</plurals>
<string name="msg_crt_error_self">Auf diese Art und Weise kann keine Eigenbeglaubigung ausgestellt werden!</string>
- <string name="msg_crt_error_master_not_found">Hauptschlüssel nicht gefunden!</string>
+ <string name="msg_crt_error_master_not_found">Hauptschlüssel wurde nicht gefunden!</string>
<string name="msg_crt_error_nothing">Keine beglaubigten Schlüssel!</string>
<string name="msg_crt_error_unlock">Fehler beim Entsperren des Hauptschlüssels!</string>
<string name="msg_crt">Schlüsselbünde werden beglaubigt</string>
@@ -1185,8 +1195,8 @@
<string name="msg_crt_save">Beglaubigter Schlüssel %s wird gespeichert</string>
<string name="msg_crt_saving">Schlüsselbünde werden gespeichert</string>
<string name="msg_crt_unlock">Hauptschlüssel wird entsperrt</string>
- <string name="msg_crt_success">Identitäten erfolgreich beglaubigt</string>
- <string name="msg_crt_warn_not_found">Schlüssel nicht gefunden!</string>
+ <string name="msg_crt_success">Identitäten wurden erfolgreich beglaubigt</string>
+ <string name="msg_crt_warn_not_found">Schlüssel wurde nicht gefunden!</string>
<string name="msg_crt_warn_cert_failed">Erzeugen der Beglaubigung fehlgeschlagen!</string>
<string name="msg_crt_warn_save_failed">Speichervorgang fehlgeschlagen!</string>
<string name="msg_crt_warn_upload_failed">Hochladevorgang fehlgeschlagen!</string>
@@ -1197,15 +1207,15 @@
</plurals>
<string name="msg_import_fetch_error_decode">Fehler beim Dekodieren des abgerufenen Schlüsselbundes!</string>
<string name="msg_import_fetch_error">Schlüssel konnte nicht abgerufen werden! (Netzwerkprobleme?)</string>
- <string name="msg_import_fetch_error_keyserver">Konnte Schlüssel nicht von Keyserver abrufen: %s</string>
+ <string name="msg_import_fetch_error_keyserver">Schlüssel konnte nicht vom Schlüsselserver abgerufen werden: %s</string>
<string name="msg_import_fetch_error_keyserver_secret">Konnte Schlüssel nicht von Keyserver importieren!</string>
<string name="msg_import_fetch_keybase">Von Keybase.io wird abgerufen: %s</string>
- <string name="msg_import_fetch_facebook">Empfange von Facebook: %s</string>
- <string name="msg_import_fetch_keyserver">Empfange von Schlüsselserver: %s</string>
- <string name="msg_import_fetch_keyserver_ok">Schlüssel erfolgreich heruntergeladen</string>
+ <string name="msg_import_fetch_facebook">Von Facebook wird abgerufen: %s</string>
+ <string name="msg_import_fetch_keyserver">Vom Schlüsselserver wird abgerufen: %s</string>
+ <string name="msg_import_fetch_keyserver_ok">Schlüssel erfolgreich abgerufen</string>
<string name="msg_import_keyserver">Verwende Schlüsselserver %s</string>
<string name="msg_import_merge">Abgerufene Daten werden eingefügt</string>
- <string name="msg_import_merge_error">Fehler beim Zusammenführen der abgerufenen Daten!</string>
+ <string name="msg_import_merge_error">Fehler beim Einfügen der abgerufenen Daten!</string>
<string name="msg_import_error">Importvorgang fehlgeschlagen!</string>
<string name="msg_import_error_io">Importvorgang ist aufgrund eines Ein-/Ausgabefehlers fehlgeschlagen!</string>
<string name="msg_import_partial">Importvorgang erfolgreich, mit Fehlern!</string>
@@ -1219,7 +1229,7 @@
<string name="msg_backup_secret">Backup von privatem Schlüssel %s wird erzeugt</string>
<string name="msg_backup_error_uri_open">Fehler beim Öffnen des URI-Streams!</string>
<string name="msg_backup_error_db">Datenbankfehler!</string>
- <string name="msg_backup_error_io">Eingabe/Ausgabe Fehler!</string>
+ <string name="msg_backup_error_io">Ein-/Ausgabefehler!</string>
<string name="msg_backup_success">Backupvorgang erfolgreich</string>
<string name="msg_upload">Öffentlicher Schlüssel wird hochgeladen</string>
<string name="msg_upload_proxy_direct">Benutze Proxy: Keinen</string>
@@ -1227,8 +1237,8 @@
<string name="msg_upload_proxy">Benutze Proxy: %s</string>
<string name="msg_upload_server">Server: %s</string>
<string name="msg_upload_key">Key ID: %s</string>
- <string name="msg_upload_error_key">Fehler bei der Vorverarbeitung der Schlüsseldaten!</string>
- <string name="msg_upload_error_not_found">Schlüssel nicht gefunden!</string>
+ <string name="msg_upload_error_key">Fehler beim Vorverarbeiten von Schlüsseldaten!</string>
+ <string name="msg_upload_error_not_found">Schlüssel wurde nicht gefunden!</string>
<string name="msg_upload_error_upload">Fehler beim Hochladen des Schlüssels zum Server! Bitte überprüfe deine Internetverbindung</string>
<string name="msg_upload_success">Hochladen auf Schlüsselserver erfolgreich</string>
<string name="msg_del_error_empty">Nichts zu löschen!</string>
@@ -1272,43 +1282,43 @@
<string name="msg_lv_fetch_error_io">Ein-/Ausgabefehler!</string>
<string name="msg_lv_fetch_error_format">Formatfehler!</string>
<string name="msg_lv_fetch_error_nothing">Ressource wurde nicht gefunden!</string>
- <string name="msg_bench">Benchmark-Test im Gang</string>
+ <string name="msg_bench">Leistungstest für einige Vorgänge wird ausgeführt...</string>
<string name="msg_bench_enc_time">Zeit zum Verschlüsseln: %sss</string>
<string name="msg_bench_enc_time_avg">Durchschnittliche Zeit zum Verschlüsseln von 5MB: %ss</string>
<string name="msg_bench_dec_time">Zeit zum Entschlüsseln: %sss</string>
<string name="msg_bench_dec_time_avg">Durchschnittliche Zeit zum Entschlüsseln von 5MB: %ss</string>
<string name="msg_bench_s2k_100ms_its">S2K Iterationen in 100ms: %s</string>
<string name="msg_bench_s2k_for_it">Zeit für %1$s SHA1 S2K Iterationen: %2$sms</string>
- <string name="msg_bench_success">Benchmark-Test beendet</string>
- <string name="msg_data">Verarbeite Eingabe</string>
- <string name="msg_data_openpgp">Versuche OpenPGP-Daten zu verarbeiten</string>
- <string name="msg_data_detached">Abgetrennte Signatur aufgetreten</string>
- <string name="msg_data_detached_clear">Lösche frühere, unsignierte Daten!</string>
- <string name="msg_data_detached_sig">Verarbeite abgetrennte Signatur</string>
- <string name="msg_data_detached_raw">Verarbeite signierte Daten</string>
- <string name="msg_data_detached_nested">Überspringe verschachtelte signierte Daten!</string>
- <string name="msg_data_detached_trailing">Überspringe Daten nach signiertem Teil!</string>
- <string name="msg_data_detached_unsupported">Typ von abgetrennter Signatur nicht unterstützt!</string>
- <string name="msg_data_error_io">Fehler beim Lesen der Daten!</string>
- <string name="msg_data_mime_bad">MIME-Daten konnten nicht verarbeitet werden</string>
+ <string name="msg_bench_success">Leistungstest wurde beendet!</string>
+ <string name="msg_data">Eingabedaten werden verarbeitet</string>
+ <string name="msg_data_openpgp">Es wird versucht OpenPGP-Daten zu verarbeiten</string>
+ <string name="msg_data_detached">Abgetrennte Signatur gefunden</string>
+ <string name="msg_data_detached_clear">Frühere unsignierte Daten werden gelöscht!</string>
+ <string name="msg_data_detached_sig">Abgetrennte Signatur wird verarbeitet</string>
+ <string name="msg_data_detached_raw">Signierte Daten werden verarbeitet</string>
+ <string name="msg_data_detached_nested">Verschachtelte signierte Daten werden übersprungen!</string>
+ <string name="msg_data_detached_trailing">Überspringe anhängende Daten nach signiertem Teil!</string>
+ <string name="msg_data_detached_unsupported">Unbekannter Typ einer abgetrennten Signatur!</string>
+ <string name="msg_data_error_io">Fehler beim Lesen der Eingabedaten!</string>
+ <string name="msg_data_mime_bad">MIME-Daten konnten nicht analysiert werden</string>
<string name="msg_data_mime_filename">Dateiname: \'%s\'</string>
- <string name="msg_data_mime_from_extension">Versuche MIME-Typ aus Dateiendung zu ermitteln</string>
+ <string name="msg_data_mime_from_extension">Es wird versucht den MIME-Typ aus der Dateiendung zu ermitteln</string>
<string name="msg_data_mime_length">Content-Länge: %s</string>
<string name="msg_data_mime_charset">Zeichensatz ist \'%s\'</string>
- <string name="msg_data_mime_charset_faulty">Zeichensatz ist \'%s\', aber Decodieren ist fehlgeschlagen.</string>
+ <string name="msg_data_mime_charset_faulty">Zeichensatz ist \'%s\', aber Dekodierung ist fehlgeschlagen!</string>
<string name="msg_data_mime_charset_guess">Zeichensatz scheint \'%s\' zu sein</string>
<string name="msg_data_mime_charset_unknown">Zeichensatz unbekannt, oder Daten sind kein Text.</string>
- <string name="msg_data_mime">Verarbeite MIME Daten Struktur</string>
- <string name="msg_data_mime_ok">Parsen beendet</string>
+ <string name="msg_data_mime">MIME-Datenstruktur wird analysiert</string>
+ <string name="msg_data_mime_ok">Analyse wurde beendet</string>
<string name="msg_data_mime_none">Keine MIME-Struktur gefunden</string>
- <string name="msg_data_mime_part">Verarbeite MIME-Daten</string>
+ <string name="msg_data_mime_part">MIME-Daten werden verarbeitet</string>
<string name="msg_data_mime_type">Content-Typ: %s</string>
<string name="msg_data_ok">Datenverarbeitung erfolgreich!</string>
- <string name="msg_data_skip_mime">Überspringe MIME-Parsing</string>
+ <string name="msg_data_skip_mime">MIME-Analyse wird übersprungen</string>
<string name="msg_acc_saved">Benutzerkonto gespeichert</string>
<string name="msg_get_success">Erfolgreich heruntergeladen!</string>
- <string name="msg_get_file_not_found">Datei nicht gefunden!</string>
- <string name="msg_get_no_valid_keys">Keine gültigen Schlüssel in Datei/Zwischenablage gefunden!</string>
+ <string name="msg_get_file_not_found">Eingabedatei wurde nicht gefunden!</string>
+ <string name="msg_get_no_valid_keys">Keine gültigen Schlüssel in der Datei/Zwischenablage gefunden!</string>
<string name="msg_get_too_many_responses">Die Schlüsselanfrage liefert zu viele Ergebnisse. Bitte präzisiere deine Suchanfrage!</string>
<string name="msg_get_query_too_short">Suchanfrage zu kurz. Bitte ändere deine Anfrage!</string>
<string name="msg_get_query_too_short_or_too_many_responses">Keine oder zu viele Schlüssel wurden gefunden. Bitte präzisiere deine Anfrage!</string>
@@ -1322,11 +1332,11 @@
<string name="msg_keybase_error_specific">%s</string>
<string name="msg_keybase_error_msg_payload_mismatch">Entschlüsselter Nachweis entspricht nicht dem erwarteten Wert</string>
<!--Messages for Mime parsing operation-->
- <string name="msg_mime_parsing_start">Parse MIME-Struktur</string>
- <string name="msg_mime_parsing_error">MIME Verarbeitung fehlgeschlagen</string>
- <string name="msg_mime_parsing_success">MIME Verarbeitung erfolgreich!</string>
+ <string name="msg_mime_parsing_start">MIME-Struktur wird analysiert</string>
+ <string name="msg_mime_parsing_error">MIME-Analyse ist fehlgeschlagen</string>
+ <string name="msg_mime_parsing_success">MIME-Analyse war erfolgreich!</string>
<!--PassphraseCache-->
- <string name="passp_cache_notif_touch_to_clear">Berühren um Passwörter zu löschen.</string>
+ <string name="passp_cache_notif_touch_to_clear">Berühren, um Passwörter zu löschen.</string>
<plurals name="passp_cache_notif_n_keys">
<item quantity="one">%d Passwort gemerkt</item>
<item quantity="other">%d Passwörter gemerkt</item>
@@ -1336,18 +1346,18 @@
<string name="passp_cache_notif_pwd">Passwort</string>
<!--Keyserver sync-->
<string name="keyserver_sync_orbot_notif_title">Synchronisierung von Servern erfordert Orbot</string>
- <string name="keyserver_sync_orbot_notif_msg">Zum Starten von Orbot tippen</string>
+ <string name="keyserver_sync_orbot_notif_msg">Berühren um Orbot zu starten</string>
<string name="keyserver_sync_orbot_notif_start">Orbot starten</string>
<string name="keyserver_sync_orbot_notif_ignore">Direkt</string>
<!--First Time-->
<string name="first_time_text1">Hol dir deine Privatsphäre mit OpenKeychain zurück!</string>
<string name="first_time_create_key">Meinen Schlüssel erzeugen</string>
<string name="first_time_import_key">Schlüssel aus Datei importieren</string>
- <string name="first_time_security_token">Verwende Smartcard</string>
+ <string name="first_time_security_token">Security-Token verwenden</string>
<string name="first_time_security_token_subtitle">(Fidesmo, YubiKey NEO, SIGILANCE, ...)</string>
<string name="first_time_skip">Setup überspringen</string>
- <string name="first_time_blank_security_token">Diese leere Smartcard mit OpenKeychain verwenden?\n\nBitte entferne die Smartcard jetzt. Du wirst aufgefordert, wenn sie erneut benötigt wird!</string>
- <string name="first_time_blank_security_token_yes">Diese Smartcard verwenden</string>
+ <string name="first_time_blank_security_token">Diesen leeren Security-Token mit OpenKeychain verwenden?\n\nBitte entferne den Security-Token jetzt. Du wirst aufgefordert, wenn er erneut benötigt wird!</string>
+ <string name="first_time_blank_security_token_yes">Diesen Security-Token verwenden</string>
<string name="backup_text">Backups, die deine eigenen Schlüssel beinhalten, dürfen unter keinen Umständen an anderen Personen gegeben werden.</string>
<string name="backup_all">Alle Schlüssel + deine eigenen Schlüssel</string>
<string name="backup_public_keys">Alle Schlüssel</string>
@@ -1362,29 +1372,29 @@
<string name="certs_text">Nur geprüfte Eigenbeglaubigungen und geprüfte Beglaubigungen, die mit deinen Schlüsseln erzeugt wurden, werden hier angezeigt.</string>
<string name="section_uids_to_certify">Identitäten für</string>
<string name="certify_text">Die zu importierenden Schlüssel enthalten \"Identitäten\": Namen und E-Mail-Adressen. Wähle genau diejenigen zum Bestätigen aus, die deinen Erwartungen entsprechen.</string>
- <string name="certify_fingerprint_text">Vergleiche den angezeigten Fingerabdruck zeichenweise mit dem, der auf dem Bildschirm des Geräts deines Partners angezeigt wird.</string>
- <string name="certify_fingerprint_text_phrases">Vergleiche den Fingerabdruck mit dem, der auf dem Gerät deines Partners angezeigt wird.</string>
+ <string name="certify_fingerprint_text">Vergleiche den angezeigten Fingerabdruck zeichenweise mit dem, der auf dem Gerät deines Gegenübers angezeigt wird.</string>
+ <string name="certify_fingerprint_text_phrases">Vergleiche diese Phrasen mit denen, die auf dem Gerät deines Gegenübers angezeigt werden.</string>
<string name="label_revocation">Widerrufsgrund</string>
<string name="label_cert_type">Typ</string>
- <string name="error_key_not_found">Schlüssel nicht gefunden!</string>
+ <string name="error_key_not_found">Schlüssel wurde nicht gefunden!</string>
<string name="error_key_processing">Fehler bei der Verarbeitung des Schlüssels!</string>
<string name="key_stripped">gekürzt</string>
- <string name="key_divert">auf Smartcard umleiten</string>
+ <string name="key_divert">auf Security-Token umleiten</string>
<string name="key_no_passphrase">kein Passwort</string>
<string name="key_unavailable">nicht verfügbar</string>
<string name="secret_cannot_multiple">Deine eigenen Schlüssel können nur einzeln gelöscht werden!</string>
<string name="title_view_cert">Beglaubigungsdetails anzeigen</string>
<string name="unknown_algorithm">unbekannt</string>
<string name="can_sign_not">kann nicht signieren</string>
- <string name="error_no_encrypt_subkey">Kein Unterschlüssel zum Verschlüsseln verfügbar!</string>
+ <string name="error_no_encrypt_subkey">Kein Verschlüsselungs-Unterschlüssel verfügbar!</string>
<string name="contact_show_key">Schlüssel anzeigen (%s)</string>
<string name="swipe_to_update">Nach unten wischen, um vom Schlüsselserver zu aktualisieren</string>
<string name="error_no_file_selected">Mindestens eine Datei zum Verschlüsseln auswählen!</string>
<string name="error_multi_files">Das speichern von mehreren Dateien wird nicht unterstützt. Dies ist eine Einschränkung der aktuellen Android Version.</string>
<string name="error_multi_clipboard">Verschlüsselung mehrerer Dateien in die Zwischenablage wird nicht unterstützt.</string>
<string name="error_detached_signature">Nur-signieren-Vorgang von Binärdateien wird nicht unterstützt, bitte mindestens einen Verschlüsselungsschlüssel auswählen.</string>
- <string name="error_empty_text">Zu verschlüsselnden Text eingeben</string>
- <string name="error_log_share_internal">Interner Fehler beim Vorbereiten des Logs!</string>
+ <string name="error_empty_text">Zu verschlüsselnden Text eingeben!</string>
+ <string name="error_log_share_internal">Interner Fehler beim Vorbereiten des Protokolls!</string>
<string name="key_colon">Schlüssel:</string>
<string name="exchange_description">Um einen Schlüsselaustausch zu starten wähle auf der rechten Seite die Teilnehmer aus, drücke dann den \"Austausch starten\"-Knopf.\n\nDu wirst zusätzlich zwei Fragen gestellt bekommen um sicherzustellen, dass nur die richtigen Teilnehmer am Austausch beteiligt sind und deren Fingerabdrücke korrekt sind.</string>
<string name="btn_start_exchange">Austausch starten</string>
@@ -1394,7 +1404,7 @@
<string name="account_privacy_title">Datenschutz</string>
<string name="account_privacy_text">OpenKeychain synchronisiert deine Kontakte nicht mit dem Internet. Es verknüpft lediglich Kontakte mit Schlüsseln auf der Basis von Namen und E-Mail-Adressen. Das alles findet offline auf deinem Gerät statt.</string>
<string name="sync_notification_permission_required_title">Zugang zu Kontakten erforderlich</string>
- <string name="sync_notification_permission_required_text">Berühren um verbundene Kontakte zu konfigurieren</string>
+ <string name="sync_notification_permission_required_text">Berühren, um Kontaktverknüpfung zu konfigurieren</string>
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">Wähle eine Entsperrmethode</string>
@@ -1425,64 +1435,64 @@
<string name="snack_security_token_import">Importieren</string>
<string name="button_bind_key">Schlüssel verbinden</string>
<string name="security_token_serial_no">Seriennummer: %s</string>
- <string name="security_token_key_holder">Schlüsseleigner:</string>
+ <string name="security_token_key_holder">Schlüsselbesitzer: %s</string>
<string name="security_token_key_holder_not_set"><![CDATA[Schlüsselinhaber: <nicht festgelegt>]]></string>
- <string name="security_token_status_bound">Smartcard stimmt überein und ist mit dem Schlüssel verbunden</string>
- <string name="security_token_status_unbound">Smartcard stimmt überein und kann mit dem Schlüssel verbunden werden</string>
- <string name="security_token_status_partly">Smartcard stimmt überein und ist teilweise mit dem Schlüssel verbunden</string>
- <string name="security_token_create">Halte die Smartcard gegen die Rückseite Deines Geräts.</string>
- <string name="security_token_reset_or_import">Diese Smartcard enthält bereits einen Schlüssel. Importiere den Schlüssel über die Cloud oder setze die Smartcard zurück.</string>
+ <string name="security_token_status_bound">Security-Token stimmt überein und ist mit dem Schlüssel verbunden</string>
+ <string name="security_token_status_unbound">Security-Token stimmt überein und kann mit dem Schlüssel verbunden werden</string>
+ <string name="security_token_status_partly">Security-Token stimmt überein und ist teilweise mit dem Schlüssel verbunden</string>
+ <string name="security_token_create">Halte den Security-Token gegen die Rückseite deines Geräts.</string>
+ <string name="security_token_reset_or_import">Dieser Security-Token enthält bereits einen Schlüssel. Du kannst den Schlüssel über die Cloud importieren oder den Security-Token zurücksetzen.</string>
<string name="btn_import">Import</string>
<string name="btn_reset">Reset</string>
<string name="security_token_import_radio">Schlüssel Importieren</string>
- <string name="security_token_reset_radio">Smartcard zurücksetzen</string>
- <string name="security_token_reset_warning">Rücksetzen der Smartcard zerstört die darauf gespeicherten Schlüssel. Mit diesen Schlüssel verschlüsselte Nachrichten/Dateien können danach nicht mehr entschlüsselt werden!</string>
- <string name="snack_security_token_other">Anderer Schlüssel auf Smartcard gespeichert!</string>
+ <string name="security_token_reset_radio">Security-Token zurücksetzen</string>
+ <string name="security_token_reset_warning">Das Zurücksetzen des Security-Tokens zerstört die darauf gespeicherten Schlüssel vollständig. Mit diesen Schlüssel verschlüsselte Nachrichten/Dateien können danach nicht mehr entschlüsselt werden!</string>
+ <string name="snack_security_token_other">Es ist ein anderer Schlüssel auf dem Security-Token gespeichert!</string>
<string name="security_token_error">Fehler: %s</string>
<plurals name="security_token_error_pin">
<item quantity="one">Falsche PIN\n%d Versuch verbleibend</item>
<item quantity="other">Falsche PIN\n%d Versuche verbleibend</item>
</plurals>
- <string name="security_token_error_terminated">Smartcard befindet sich in beendetem Zustand.</string>
+ <string name="security_token_error_terminated">Der Security-Token befindet sich in beendetem Zustand.</string>
<string name="security_token_error_wrong_length">Eingegebene PIN zu kurz. PINs müssen mindestens 6 Ziffern lang sein.</string>
<string name="security_token_error_conditions_not_satisfied">Nutzungsbedingungen werden nicht erfüllt. </string>
<string name="security_token_error_security_not_satisfied">Sicherheitsstatus nicht erfüllt.</string>
<string name="security_token_error_authentication_blocked">PIN nach zu vielen Versuchen gesperrt.</string>
- <string name="security_token_error_data_not_found">Schlüssel oder Objekt nicht gefunden!</string>
+ <string name="security_token_error_data_not_found">Schlüssel oder Objekt wurde nicht gefunden!</string>
<string name="security_token_error_unknown">Unbekannter Fehler</string>
- <string name="security_token_error_bad_data">Smartcard meldete ungültige Daten.</string>
- <string name="security_token_error_chaining_error">Smartcard erwartete das letzte Kommando in einer Kette.</string>
- <string name="security_token_error_header">Smartcard meldete ungültige %s Byte.</string>
- <string name="security_token_error_tag_lost">Smartcard wurde zu früh abgenommen. Halte die Smartcard an die Rückseite bis der Vorgang beendet ist.</string>
+ <string name="security_token_error_bad_data">Der Security-Token meldete ungültige Daten.</string>
+ <string name="security_token_error_chaining_error">Der Security-Token erwartete das letzte Kommando in einer Reihe.</string>
+ <string name="security_token_error_header">Der Security-Token meldete ungültige %s Byte.</string>
+ <string name="security_token_error_tag_lost">Security-Token wurde zu früh abgenommen. Halte den Security-Token an die Rückseite, bis der Vorgang beendet ist.</string>
<string name="security_token_error_iso_dep_not_supported">Smartcard unterstützt ISO-DEP (ISO 14443-4) nicht</string>
<string name="security_token_error_try_again">Erneut versuchen</string>
<string name="btn_delete_original">Originaldatei löschen</string>
<string name="snack_encrypt_filenames_on">Dateinamen <b>sind</b> verschlüsselt.</string>
<string name="snack_encrypt_filenames_off">Dateinamen <b>sind nicht</b> verschlüsselt.</string>
- <string name="snack_armor_on">Ausgabe als Text kodiert.</string>
- <string name="snack_armor_off">Ausgabe als Binärdatei kodiert.</string>
+ <string name="snack_armor_on">Ausgabe wurde als Text kodiert.</string>
+ <string name="snack_armor_off">Ausgabe wurde als Binärdatei kodiert.</string>
<string name="snack_compression_on">Komprimierung ist <b>aktiviert</b>.</string>
<string name="snack_compression_off">Komprimierung ist <b>deaktiviert</b>.</string>
<string name="error_loading_keys">Fehler beim Laden der Schlüssel!</string>
<string name="error_empty_log">(Fehler, Protokoll leer)</string>
<string name="error_reading_text">Konnte Eingabe zur Entschlüsselung nicht lesen!</string>
- <string name="error_reading_aosp">Daten konnten nicht gelesen, dies ist ein Bug im Android E-Mail-Client! (Issue #290)</string>
- <string name="error_reading_k9">Unvollständige Daten erhalten. Versuche \'Vollständige Nachricht herunterladen\' in K-9 Mail zu drücken.</string>
- <string name="filename_unknown">Unbekanter Dateiname (Zum Öffnen berühren)</string>
- <string name="filename_unknown_text">Text (Zum Anzeigen berühren)</string>
- <string name="filename_keys">Schlüssel Sicherheitskopie (berühren zum importieren)</string>
- <string name="intent_show">Signierten/verschlüsselten Inhalt anzeigen</string>
+ <string name="error_reading_aosp">Daten konnten nicht gelesen werden, dies ist ein Fehler im Android E-Mail-Client (Fehler #290)!</string>
+ <string name="error_reading_k9">Unvollständige Daten empfangen, versuche die Funktion \'Gesamte Nachricht herunterladen\' in K-9 Mail zu berühren.</string>
+ <string name="filename_unknown">Unbekannter Dateiname (zum Öffnen berühren)</string>
+ <string name="filename_unknown_text">Text (zum Anzeigen berühren)</string>
+ <string name="filename_keys">Schlüsselbackup (zum Importieren berühren)</string>
+ <string name="intent_show">Signierten/Verschlüsselten Inhalt anzeigen</string>
<string name="intent_share">Signierten/Verschlüsselten Inhalt teilen</string>
<string name="view_internal">In OpenKeychain anzeigen</string>
<string name="error_preparing_data">Fehler beim Vorbereiten der Daten!</string>
<string name="label_clip_title">Verschlüsselte Daten</string>
<string name="progress_processing">Wird verarbeitet...</string>
<string name="error_saving_file">Fehler beim Speichern der Datei!</string>
- <string name="file_saved">Datei gespeichert!</string>
+ <string name="file_saved">Datei wurde gespeichert!</string>
<string name="file_delete_ok">Originaldatei gelöscht.</string>
<string name="file_delete_none">Keine Datei gelöscht! (bereits gelöscht?)</string>
<string name="file_delete_exception">Originaldatei konnte nicht gelöscht werden!</string>
- <string name="error_clipboard_empty">Zwischenablage ist leer!</string>
+ <string name="error_clipboard_empty">Die Zwischenablage ist leer!</string>
<string name="error_clipboard_copy">Fehler beim Kopieren der Daten in die Zwischenablage!</string>
<string name="error_scan_fp">Fehler beim Scannen des Fingerabdrucks!</string>
<string name="error_scan_match">Fingerabdrücke stimmten nicht überein!</string>
@@ -1495,15 +1505,15 @@
<string name="linked_create_https_2_1">Eine Nachweisdatei für diesen URI wurde erzeugt:</string>
<string name="linked_create_https_2_2">Im nächsten Schritt solltest du Speichern und diese Datei hochladen.</string>
<string name="linked_create_https_2_3">Stelle sicher dass die Datei unter der korrekten URI erreichbar ist, prüfe danach deine Einstellungen.</string>
- <string name="linked_create_https_2_4">Drücke nach erfolgreicher Verifikation auf Abschließen, um die Verknüpfte-Identität deinem Schlüsselbund hinzuzufügen und den Vorgang zu beenden.</string>
+ <string name="linked_create_https_2_4">Berühre nach erfolgreicher Verifikation \'Abschließen\', um die Verknüpfte-Identität deinem Schlüsselbund hinzuzufügen und den Vorgang zu beenden.</string>
<string name="linked_create_twitter_1_1">Durch das Erzeugen einer Verknüpften-Identität dieses Typs kannst du deinen Schlüssel mit einem Twitter-Benutzerkonto verknüpfen, das du kontrollierst.</string>
<string name="linked_create_twitter_1_2">Um das zu tun veröffentlichst du einen bestimmten Tweet in deiner Chronik, anschließend erzeugst du eine Verknüpfte-Identität, die auf diesen Tweet verweist.</string>
<string name="linked_create_twitter_1_3">Zum Fortfahren gib bitte deinen Twitter-Namen an.</string>
<string name="linked_create_twitter_handle">Twitter-Handle</string>
- <string name="linked_create_twitter_2_1">Drücke einen der Knöpfe, um den Tweet abzusenden!</string>
- <string name="linked_create_twitter_2_2">Du kannst den Tweet vor dem Absenden beliebig ändern, solange der Text in den Klammern unverändert bleibt.</string>
- <string name="linked_create_twitter_2_3">Sobald dein Tweet als &lt;b&gt;@%s&lt;/b&gt; veröffentlicht wurde, klicke die Verifizieren-Schaltfläche, um deine Chronik danach zu durchsuchen.</string>
- <string name="linked_create_twitter_2_4">Drücke nach erfolgreicher Verifikation auf Abschließen, um die verknüpfte Identität deinem Schlüsselbund hinzuzufügen und den Vorgang zu beenden.</string>
+ <string name="linked_create_twitter_2_1">Berühre einen der Knöpfe, um den Tweet abzusenden!</string>
+ <string name="linked_create_twitter_2_2">Du kannst den Tweet vor dem Absenden beliebig bearbeiten, solange der Text in Klammern unverändert bleibt.</string>
+ <string name="linked_create_twitter_2_3">Sobald dein Tweet als &lt;b&gt;@%s&lt;/b&gt; veröffentlich wurde, berühre die Verifizieren-Schaltfläche, um deine Chronik danach zu durchsuchen.</string>
+ <string name="linked_create_twitter_2_4">Berühre nach erfolgreicher Verifikation \'Abschließen\', um die Verknüpfte-Identität deinem Schlüsselbund hinzuzufügen und den Vorgang zu beenden.</string>
<string name="linked_create_verify">Verifizieren</string>
<string name="linked_text_clipboard">Text wurde in die Zwischenablage kopiert</string>
<string name="linked_verified_https">Die Verknüpfung zwischen dieser Webseite und dem Schlüssel wurde sicher verifiziert. <b>Wenn du glaubst dass die Webseite echt ist</b>, bestätige die Verifikation mit deinem Schlüssel.</string>
@@ -1519,7 +1529,7 @@
<item quantity="other">Es gibt %d weitere unbekannte Identitätstypen</item>
</plurals>
<!--Other Linked Identity strings-->
- <string name="linked_select_1">Eine \"Verknüpfte Identität\" verbindet deinen PGP-Schlüssel mit einem Dienst im Internet.</string>
+ <string name="linked_select_1">Eine \'Verknüpfte-Identität\' verbindet deinen PGP-Schlüssel mit einem Dienst im Internet.</string>
<string name="linked_select_2">Bitte wähle einen Typ aus:</string>
<string name="linked_id_generic_text">Diese Datei erhebt Anspruch auf den Besitz des OpenPGP-Schlüssels mit der langen ID %2$s.\n\nToken des Nachweises:\n%1$s</string>
<string name="linked_id_github_text">Dieses Gist bestätigt die Verknüpfte-Identität innerhalb meines OpenPGP-Schlüssels und verknüpft es mit diesem GitHub-Benutzerkonto.\nToken des Nachweises:\n%1$s</string>
@@ -1546,16 +1556,16 @@
<string name="linked_text_confirming">Wird bestätigt...</string>
<string name="linked_ids_more_unknown">%d weitere unbekannte Identitätstypen</string>
<string name="title_linked_id_create">Verknüpfte-Identität erzeugen</string>
- <string name="linked_github_text">Dieser Vorgang verknüpft deinen Schlüssel mit deinem GitHub-Konto.\nBerühre den Knopf um fortzufahren.</string>
- <string name="linked_progress_auth_github">Mit GitHub authorisieren</string>
+ <string name="linked_github_text">Dieser Vorgang verknüpft deinen Schlüssel mit deinem GitHub-Benutzerkonto.\nBerühre den Knopf um fortzufahren.</string>
+ <string name="linked_progress_auth_github">Mit GitHub authorisieren...</string>
<string name="linked_progress_post_gist">Gist wird veröffentlicht...</string>
<string name="linked_progress_update_key">Aktualisiere Schlüssel...</string>
- <string name="linked_button_start">Mit Github-Konto verknüpfen</string>
- <string name="linked_error_auth_failed">Authentifizierung fehlgeschlagen!</string>
+ <string name="linked_button_start">Mit Github-Benutzerkonto verknüpfen</string>
+ <string name="linked_error_auth_failed">Authorisierung ist fehlgeschlagen!</string>
<string name="linked_error_timeout">Zeitüberschreitung beim Verbindungsaufbau!</string>
<string name="linked_error_network">Netzwerkfehler!</string>
<string name="linked_error_http">Kommunikationsfehler: %s</string>
- <string name="linked_webview_title_github">GitHub Authentifizierung</string>
+ <string name="linked_webview_title_github">GitHub-Authorisierung</string>
<string name="linked_gist_description">Verknüpfte OpenKeychain-Identität</string>
<string name="linked_empty">Verknüpfe deinen Schlüssel mit GitHub, Twitter oder anderen Websites!</string>
<string name="snack_btn_overwrite">Überschreiben</string>
@@ -1569,21 +1579,21 @@
<string name="snack_backup_error_saving">Fehler beim Speichern des Backups!</string>
<string name="snack_backup_saved">Backup gespeichert</string>
<string name="snack_backup_exists">Backup existiert bereits!</string>
- <string name="snack_backup_saved_dir">Gespeichert im OpenKeychain Ordner</string>
+ <string name="snack_backup_saved_dir">In den OpenKeychain-Ordner gespeichert</string>
<string name="btn_backup_back">Zurück zum Überprüfen</string>
<string name="snack_text_too_long">Text ist zu lange um vollständig angezeigt zu werden</string>
<string name="snack_shared_text_too_long">Geteilter Text wurde gekürzt, weil er zu lange ist!</string>
- <string name="share_log_dialog_title">Log teilen?</string>
- <string name="share_log_dialog_message">Logs können sehr hilfreich für Entwickler sein, um Fehler in OpenKeychain zu finden. Sie können aber auch private Informationen über die aktualisierten Schlüssel enthalten. Vergewissere dich dass das Teilen solcher Informationen für dich in Ordnung ist.</string>
+ <string name="share_log_dialog_title">Protokoll teilen?</string>
+ <string name="share_log_dialog_message">Protokolle können sehr hilfreich für Entwickler sein, um Fehler in OpenKeychain zu finden. Sie können aber auch potentiell private Informationen, wie Daten über aktualisierte Schlüssel enthalten. Vergewissere dich, dass die Weitergabe solcher Informationen für dich in Ordnung ist.</string>
<string name="share_log_dialog_share_button">Teilen</string>
<string name="share_log_dialog_cancel_button">Abbrechen</string>
- <string name="toast_wrong_mimetype">Falscher Datentyp, Text erwartet!</string>
+ <string name="toast_wrong_mimetype">Falscher Datentyp, es wurde Text erwartet!</string>
<string name="toast_no_text">Kein Text in den geteilten Daten!</string>
<string name="menu_uids_save">Speichern</string>
<string name="title_edit_identities">Identitäten bearbeiten</string>
<string name="title_edit_subkeys">Unterschlüssel bearbeiten</string>
<string name="btn_search_for_query">Suche nach\n\'%s\'</string>
- <string name="cache_ttl_lock_screen">bis Bildschirm abgeschalten</string>
+ <string name="cache_ttl_lock_screen">bis Bildschirm abschaltet</string>
<string name="cache_ttl_ten_minutes">für 10 Minuten</string>
<string name="cache_ttl_thirty_minutes">für 30 Minuten</string>
<string name="cache_ttl_one_hour">für eine Stunde</string>
@@ -1595,7 +1605,7 @@
<string name="settings_cache_ttl_at_least_one">Mindestens eine Option muss gewählt werden!</string>
<string name="settings_cache_ttl_max_three">Höchstens 3 Optionen können gewählt werden!</string>
<string name="remember">Merken</string>
- <string name="security_token_error_pgp_app_not_installed">Keine PGP-Anwendung auf Smartcard gefunden</string>
+ <string name="security_token_error_pgp_app_not_installed">Es wurde keine PGP-Anwendung auf dem Security-Token gefunden</string>
<string name="prompt_fidesmo_pgp_install_title">PGP installieren?</string>
<string name="prompt_fidesmo_pgp_install_message">Keine PGP-Anwendung verfügbar auf diesem Fidesmo-Gerät.</string>
<string name="prompt_fidesmo_pgp_install_button_positive">Installieren</string>
@@ -1607,6 +1617,7 @@
<string name="help_donation_paypal_item">OpenKeychain Spende</string>
<string-array name="help_donation_google_catalog_values">
<item>1 EUR</item>
+ <item>2 EUR</item>
<item>3 EUR</item>
<item>5 EUR</item>
<item>10 EUR</item>
diff --git a/OpenKeychain/src/main/res/values-es-rMX/strings.xml b/OpenKeychain/src/main/res/values-es-rMX/strings.xml
index f60e2bbdf..4110883e1 100644
--- a/OpenKeychain/src/main/res/values-es-rMX/strings.xml
+++ b/OpenKeychain/src/main/res/values-es-rMX/strings.xml
@@ -203,21 +203,11 @@
<string name="choice_8hours">8 horas</string>
<string name="choice_forever">para siempre</string>
<string name="choice_select_cert">Elija una clave</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Abrir...</string>
<string name="error">Error</string>
<string name="error_message">Error: %s</string>
<string name="theme_dark">Oscuro</string>
<string name="theme_light">Claro</string>
- <!--key flags-->
- <string name="flag_certify">Certificar</string>
- <string name="flag_sign">Firmar</string>
- <string name="flag_encrypt">Cifrar</string>
- <string name="flag_authenticate">Autenticar</string>
<!--sentences-->
<string name="wrong_passphrase">Contraseña incorrecta.</string>
<string name="no_filemanager_installed">No hay instalado un administrador de archivos compatible.</string>
diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml
index 6e81e12bd..dbea7dd03 100644
--- a/OpenKeychain/src/main/res/values-es/strings.xml
+++ b/OpenKeychain/src/main/res/values-es/strings.xml
@@ -234,21 +234,11 @@
<string name="choice_8hours">8 horas</string>
<string name="choice_forever">para siempre</string>
<string name="choice_select_cert">Seleccione una clave</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Abrir...</string>
<string name="error">Error</string>
<string name="error_message">Error: %s</string>
<string name="theme_dark">Oscuro</string>
<string name="theme_light">Claro</string>
- <!--key flags-->
- <string name="flag_certify">Certificar</string>
- <string name="flag_sign">Firmar</string>
- <string name="flag_encrypt">Cifrar</string>
- <string name="flag_authenticate">Autentificar</string>
<!--sentences-->
<string name="wrong_passphrase">Contraseña incorrecta.</string>
<string name="no_filemanager_installed">No hay un gestor de archivos compatible instalado.</string>
@@ -636,7 +626,6 @@
<string name="edit_key_edit_user_id_revoked">Esta identidad se ha revocado. Esto no se puede deshacer.</string>
<string name="edit_key_edit_subkey_title">¡Seleccione una acción!</string>
<string name="edit_key_new_subkey">nueva subclave</string>
- <string name="edit_key_select_flag">¡Por favor, seleccione al menos un indicador!</string>
<string name="edit_key_error_add_identity">¡Añada al menos una identidad!</string>
<string name="edit_key_error_add_subkey">¡Añadir al menos una subclave!</string>
<!--Create key-->
diff --git a/OpenKeychain/src/main/res/values-eu/strings.xml b/OpenKeychain/src/main/res/values-eu/strings.xml
index d74485cf5..9965df050 100644
--- a/OpenKeychain/src/main/res/values-eu/strings.xml
+++ b/OpenKeychain/src/main/res/values-eu/strings.xml
@@ -8,6 +8,7 @@
<string name="title_encrypt_files">Enkriptatu</string>
<string name="title_decrypt">Dekriptatu</string>
<string name="title_add_subkey">Gehitu azpigiltza</string>
+ <string name="title_change_master_key">Aldatu giltza maisua</string>
<string name="title_edit_key">Editatu Giltza</string>
<string name="title_linked_create">Sortu Loturatutako Nortasuna</string>
<string name="title_preferences">Ezarpenak</string>
@@ -154,6 +155,7 @@
<string name="label_keyservers">Hautatu OpenPGP giltza-zerbitzariak</string>
<string name="label_key_id">Giltza ID-a</string>
<string name="label_key_created">Giltza sortuta %s</string>
+ <string name="label_key_type">Mota</string>
<string name="label_creation">Sortzea</string>
<string name="label_expiry">Epemuga</string>
<string name="label_usage">Erabilpena</string>
@@ -187,6 +189,7 @@
<string name="label_sync_settings_keyserver_title">Berezgaitasunez eguneratu giltzak</string>
<string name="label_sync_settings_keyserver_summary_on">Hiru egunetik behin, giltzak hobetsitako giltza-zerbitzaritik eguneratzen dira</string>
<string name="label_sync_settings_keyserver_summary_off">Giltzak ez dira berezgaitasunez eguneratzen</string>
+ <string name="label_sync_settings_wifi_title">Aldiberetu Wi-Fi moduan bakarrik</string>
<string name="label_sync_settings_contacts_title">Lotu giltzak harremanekin</string>
<string name="label_sync_settings_contacts_summary_on">Lotu giltzak harremanekin izen eta post@ helbideetan ohinarrituz. Hau erabat lineaz-kanpo gertatzen da zure gailuan.</string>
<string name="label_sync_settings_contacts_summary_off">Giltza berriak ez dira harremanekin lotuko</string>
@@ -254,21 +257,22 @@
<string name="choice_8hours">8 ordu</string>
<string name="choice_forever">betirako</string>
<string name="choice_select_cert">Hautatu Giltza bat</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Ireki...</string>
+ <string name="rsa_2048">RSA 2048</string>
+ <string name="rsa_2048_description_html">agiri neurri txikiagoa, segurutzat hartzen da 2030 arte</string>
+ <string name="rsa_3072">RSA 3072</string>
+ <string name="rsa_3072_description_html">gomendatua, segurutzat hartzen da 2040 arte</string>
+ <string name="rsa_4096">RSA 4096</string>
+ <string name="rsa_4096_description_html">agiri neurria handiagoa, segurutzat hartzen da 2040+ arte</string>
+ <string name="ecc_p256">ECC P-256</string>
+ <string name="ecc_p521">ECC P-521</string>
+ <string name="usage_sign">Sinatu</string>
+ <string name="usage_encrypt">Enkriptatu</string>
+ <string name="usage_sign_and_encrypt">Sinatu eta Enkriptatu</string>
<string name="error">Akatsa</string>
<string name="error_message">Akatsa: %s</string>
<string name="theme_dark">Iluna</string>
<string name="theme_light">Argia</string>
- <!--key flags-->
- <string name="flag_certify">Egiaztatu</string>
- <string name="flag_sign">Sinatu</string>
- <string name="flag_encrypt">Enkriptatu</string>
- <string name="flag_authenticate">Egiaztatu</string>
<!--sentences-->
<string name="wrong_passphrase">Sarhitz okerra.</string>
<string name="no_filemanager_installed">Ez dago agiri kudeatzaile bateragarririk ezarrita.</string>
@@ -321,6 +325,7 @@
<string name="error_file_delete_failed">ez da ezabatu. Ezabatu ezazu eskuz!</string>
<string name="error_file_added_already">%s jadanik gehitu da.</string>
<string name="error_file_not_found">agiria ez da aurkitu</string>
+ <string name="error_bad_data">Datu okerrak!</string>
<string name="error_no_secret_key_found">ez da giltza sekretu erabilgarririk aurkitu</string>
<string name="error_external_storage_not_ready">kanpoko biltegia ez dago gertu</string>
<string name="error_key_size_minimum512bit">giltzaren neurria gutxienez 512bitekoa izan behar da</string>
@@ -669,7 +674,7 @@
<item>Mugitu Azpigiltza Segurtasun Lekukora</item>
</string-array>
<string name="edit_key_new_subkey">azpigiltza berria</string>
- <string name="edit_key_select_flag">Mesedez hautatu gutxienez ikur bat!</string>
+ <string name="edit_key_select_usage">Mesedez hautatu giltza erabilpena!</string>
<string name="edit_key_error_add_identity">Gehitu nortasun bat gutxienez!</string>
<string name="edit_key_error_add_subkey">Gehitu azpigiltza bat gutxienez!</string>
<string name="edit_key_error_bad_security_token_algo">Algoritmoa ez dago Segurtasun Lekukoak sostengatua!</string>
@@ -892,6 +897,7 @@
<string name="msg_cr_error_no_user_id">Giltza-uztaiak gutxienez erabiltzaile ID batekin sortu behar dira!</string>
<string name="msg_cr_error_no_certify">Maisu giltzak egiaztagiri ikurra izan behar du!</string>
<string name="msg_cr_error_null_expiry">Epemuga ezin daiteke giltza sortzea baino \'lehenago\' izan. Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
+ <string name="msg_cr_error_keysize_2048">Giltza neurria 2048 edo handiagoa izan behar da!</string>
<string name="msg_cr_error_no_curve">Ez da giltzaren neurria adierazi! Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
<string name="msg_cr_error_internal_pgp">Barneko OpenPGP akatsa!</string>
<string name="msg_cr_error_unknown_algo">Algoritmo ezezaguna hautatu da! Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
@@ -1199,6 +1205,7 @@
<string name="msg_data_mime_bad">Ezin da MIME datu bezala aztertu</string>
<string name="msg_data_mime_filename">Agirizena: \'%s\'</string>
<string name="msg_data_mime_length">Edukia-Luzera: %s</string>
+ <string name="msg_data_mime_charset">Hizkikodea \'%s\' da</string>
<string name="msg_data_mime_charset_unknown">Hizkikodea ezezaguna da, edo datua ez da idazkia.</string>
<string name="msg_data_mime">MIME datu egitura aztertzen</string>
<string name="msg_data_mime_ok">Azterketa amaituta</string>
@@ -1495,6 +1502,7 @@
<string name="help_donation_paypal_item">OpenKeychain Dirulaguntza</string>
<string-array name="help_donation_google_catalog_values">
<item>1 EUR</item>
+ <item>2 EUR</item>
<item>3 EUR</item>
<item>5 EUR</item>
<item>10 EUR</item>
diff --git a/OpenKeychain/src/main/res/values-fa/strings.xml b/OpenKeychain/src/main/res/values-fa/strings.xml
index 837d96bf2..388e08057 100644
--- a/OpenKeychain/src/main/res/values-fa/strings.xml
+++ b/OpenKeychain/src/main/res/values-fa/strings.xml
@@ -142,10 +142,6 @@
<!--InstallDialogFragment strings-->
<!--StartOrbotDialogFragment strings-->
<!--choice-->
- <!--key flags-->
- <string name="flag_sign">امضاء کردن</string>
- <string name="flag_encrypt">رمزگذاری</string>
- <string name="flag_authenticate">تصدیق کردن</string>
<!--sentences-->
<string name="wrong_passphrase">رمزعبور اشتباه است.</string>
<string name="no_filemanager_installed">برنامهٔ مدیریتِ فایل سازگاری نصب نیست.</string>
@@ -252,7 +248,6 @@
<string name="edit_key_edit_user_id_revoked">این هویت لغو شده‌است. انصراف از این عمل شدنی نیست.</string>
<string name="edit_key_edit_subkey_title">یک عمل را انتخاب کنید!</string>
<string name="edit_key_new_subkey">زیرکلیدِ جدید</string>
- <string name="edit_key_select_flag">لطفاً حداقل یک پرچم انتخاب کنید!</string>
<string name="edit_key_error_add_identity">حداقل یک هویت اصافه کنید!</string>
<string name="edit_key_error_add_subkey">حداقل یک زیر‌کلید اضافه کنید</string>
<!--Create key-->
diff --git a/OpenKeychain/src/main/res/values-fi/strings.xml b/OpenKeychain/src/main/res/values-fi/strings.xml
index 912c34755..7a1ee8257 100644
--- a/OpenKeychain/src/main/res/values-fi/strings.xml
+++ b/OpenKeychain/src/main/res/values-fi/strings.xml
@@ -159,21 +159,11 @@
<string name="choice_8hours">8 tuntia</string>
<string name="choice_forever">ikuisesti</string>
<string name="choice_select_cert">Valitse avain</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Avaa...</string>
<string name="error">Virhe</string>
<string name="error_message">Virhe: %s</string>
<string name="theme_dark">Tumma</string>
<string name="theme_light">Vaalea</string>
- <!--key flags-->
- <string name="flag_certify">Varmenna</string>
- <string name="flag_sign">Allekirjoita</string>
- <string name="flag_encrypt">Salaa</string>
- <string name="flag_authenticate">Autentikoi</string>
<!--sentences-->
<string name="wrong_passphrase">Väärä salasana</string>
<string name="no_filemanager_installed">Yhteensopivaa tiedostonhallintaa ei ole asennettu.</string>
diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml
index 6534a41d4..dcbff56e4 100644
--- a/OpenKeychain/src/main/res/values-fr/strings.xml
+++ b/OpenKeychain/src/main/res/values-fr/strings.xml
@@ -8,6 +8,7 @@
<string name="title_encrypt_files">Chiffrer</string>
<string name="title_decrypt">Déchiffrer</string>
<string name="title_add_subkey">Ajouter une sous-clef</string>
+ <string name="title_change_master_key">Changer la clef maîtresse</string>
<string name="title_edit_key">Modifier une clef</string>
<string name="title_linked_create">Créer une identité reliée</string>
<string name="title_preferences">Paramètres</string>
@@ -35,6 +36,7 @@
<string name="title_advanced_key_info">Avancées</string>
<string name="title_delete_secret_key">Supprimer VOTRE clef « %s » ?</string>
<string name="title_manage_my_keys">Gérer mes clefs</string>
+ <string name="title_alert_strip">Dépouiller cette sous-clef</string>
<!--section-->
<string name="section_user_ids">identités</string>
<string name="section_security_token">Jeton de sécurité</string>
@@ -154,6 +156,7 @@
<string name="label_keyservers">Choisir les serveurs de clefs OpenPGP</string>
<string name="label_key_id">ID de clef</string>
<string name="label_key_created">Clef créée %s</string>
+ <string name="label_key_type">Type</string>
<string name="label_creation">Création</string>
<string name="label_expiry">Expiration</string>
<string name="label_usage">Utilisation</string>
@@ -166,10 +169,10 @@
<string name="label_send_key">Synchroniser par l\'Internet</string>
<string name="label_fingerprint">Empreinte</string>
<string name="expiry_date_dialog_title">Définir une date d\'expiration</string>
- <string name="label_keyservers_title">Serveurs de clefs</string>
+ <string name="label_keyservers_title">Serveurs de clefs </string>
<string name="label_keyserver_settings_hint">Glisser pour changer l\'ordre, toquer pour éditer/supprimer</string>
<string name="label_selected_keyserver_title">Serveurs de clefs sélectionnés</string>
- <string name="label_preferred">préféré</string>
+ <string name="label_preferred">préféré </string>
<string name="label_enable_compression">Activer la compression</string>
<string name="label_encrypt_filenames">Chiffrer les nom de fichier</string>
<string name="label_hidden_recipients">Cacher les destinataires</string>
@@ -187,13 +190,14 @@
<string name="label_sync_settings_keyserver_title">Mises à jour automatiques des clefs</string>
<string name="label_sync_settings_keyserver_summary_on">Tous les trois jours, les clefs sont mises à jour à partir du serveur de clefs préféré</string>
<string name="label_sync_settings_keyserver_summary_off">Les clefs ne sont pas mises à jour automatiquement</string>
+ <string name="label_sync_settings_wifi_title">Synchro Wi-Fi seulement</string>
<string name="label_sync_settings_contacts_title">Relier les clefs aux contacts</string>
<string name="label_sync_settings_contacts_summary_on">Relier les clefs aux contacts d\'après les noms et les adresses courriel. Cela se passe entièrement hors ligne sur votre appareil.</string>
<string name="label_sync_settings_contacts_summary_off">Les nouvelles clefs ne seront pas reliées aux contacts</string>
<!--label shown in Android settings under the OpenKeychain account-->
<string name="keyserver_sync_settings_title">Mises à jour automatiques des clefs</string>
<string name="label_experimental_settings_desc_title">Avertissement</string>
- <string name="label_experimental_settings_desc_summary">Ces fonctions ne sont pas encore complétées et n\'ont pas fait l\'objet de recherche sur leur convivialité ni leur sécurité. Par conséquent, ne vous fiez pas à leur sécurité et veuillez ne pas rapporter les problèmes que vous rencontrez.</string>
+ <string name="label_experimental_settings_desc_summary">Ces fonctions ne sont pas encore terminées et n\'ont pas fait l\'objet de recherche sur leur convivialité, ni leur sécurité. Par conséquent, ne vous fiez pas à leur sécurité et veuillez ne pas rapporter les problèmes que vous rencontrez.</string>
<string name="label_experimental_settings_word_confirm_title">Confirmer par des phrases</string>
<string name="label_experimental_settings_word_confirm_summary">Confirmer les clefs par des phrases au lieu d\'empreintes hexadécimales</string>
<string name="label_experimental_settings_linked_identities_title">Identités reliées</string>
@@ -254,21 +258,26 @@
<string name="choice_8hours">8 heures</string>
<string name="choice_forever">pour toujours</string>
<string name="choice_select_cert">Choisir une clef</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Ouvrir...</string>
+ <string name="rsa_2048">RSA 2048</string>
+ <string name="rsa_2048_description_html">taille de fichier moindre, jugée sûre jusqu\'à 2030</string>
+ <string name="rsa_3072">RSA 3072</string>
+ <string name="rsa_3072_description_html">recommandée, jugée sûre jusqu\'à 2040</string>
+ <string name="rsa_4096">RSA 4096</string>
+ <string name="rsa_4096_description_html">taille de fichier plus grande, jugée sûre jusqu\'à 2040+</string>
+ <string name="ecc_p256">ECC P-256</string>
+ <string name="ecc_p256_description_html">très petite taille de fichier, jugée sûre jusqu\'à 2040 &lt;br/&gt; &lt;u&gt;expérimentale et non prise en charge par toutes les versions&lt;/u&gt;</string>
+ <string name="ecc_p521">ECC P-521</string>
+ <string name="ecc_p521_description_html">petite taille de fichier, jugée sûre jusqu\'à 2040+ &lt;br/&gt; &lt;u&gt;expérimentale et non prise en charge par toutes les versions"&amp;lt;/u&gt;</string>
+ <string name="usage_none">Aucune (liaison de la sous-clef seulement)</string>
+ <string name="usage_sign">Signer</string>
+ <string name="usage_encrypt">Chiffrer</string>
+ <string name="usage_sign_and_encrypt">Signer et chiffrer</string>
<string name="error">Erreur</string>
<string name="error_message">Erreur : %s</string>
<string name="theme_dark">Sombre</string>
<string name="theme_light">Clair</string>
- <!--key flags-->
- <string name="flag_certify">Certifier</string>
- <string name="flag_sign">Signer</string>
- <string name="flag_encrypt">Chiffrer</string>
- <string name="flag_authenticate">Authentifier</string>
+ <string name="strip">La dépouiller</string>
<!--sentences-->
<string name="wrong_passphrase">Mot de passe erroné.</string>
<string name="no_filemanager_installed">Aucun gestionnaire de fichiers compatible installé.</string>
@@ -302,6 +311,7 @@
<string name="public_key_deletetion_confirmation">Supprimer la clef « %s » ?</string>
<string name="also_export_secret_keys">Importer aussi les clefs secrètes</string>
<string name="reinstall_openkeychain">Vous venez de rencontrer un bogue connu d\'Android. Veuillez réinstaller OpenKeychain si voulez relier vos contacts avec des clefs.</string>
+ <string name="alert_strip">Dépouiller cette sous-clef la rendra inutilisable sur cet appareil !</string>
<string name="key_exported">1 clef exportée avec succès.</string>
<string name="keys_exported">%d clefs exportées avec succès.</string>
<string name="no_keys_exported">Aucune clef exportée.</string>
@@ -324,6 +334,7 @@
<string name="error_file_delete_failed">n\'ont pas été supprimés. Les supprimer maintenant ?</string>
<string name="error_file_added_already">%s a déjà été supprimé.</string>
<string name="error_file_not_found">fichier introuvable</string>
+ <string name="error_bad_data">Mauvaises données !</string>
<string name="error_no_secret_key_found">aucune clefs secrète adéquate n\'a été trouvée</string>
<string name="error_external_storage_not_ready">le stockage externe n\'est pas prêt</string>
<string name="error_key_size_minimum512bit">la taille de la clef doit être d\'au moins 512 bits</string>
@@ -675,7 +686,7 @@
<item>Déplacer la sous-clef vers le jeton de sécurité</item>
</string-array>
<string name="edit_key_new_subkey">nouvelle sous-clef</string>
- <string name="edit_key_select_flag">Veuillez sélectionner au moins un drapeau !</string>
+ <string name="edit_key_select_usage">Veuillez choisir l\'utilisation de la clef !</string>
<string name="edit_key_error_add_identity">Ajouter au moins une identité !</string>
<string name="edit_key_error_add_subkey">Ajouter au moins une sous-clef !</string>
<string name="edit_key_error_bad_security_token_algo">L’algorithme n\'est pas pris en charge par le jeton de sécurité !</string>
@@ -1342,7 +1353,7 @@
<string name="first_time_text1">Reprenez le contrôle de votre vie privée avec OpenKeychain |</string>
<string name="first_time_create_key">Créer ma clef</string>
<string name="first_time_import_key">Importer la clef d\'un fichier</string>
- <string name="first_time_security_token">Utiliser le jeton de sécurité</string>
+ <string name="first_time_security_token">Utiliser un jeton de sécurité</string>
<string name="first_time_security_token_subtitle">(Fidesmo, YubiKey NEO, SIGILANCE, …)</string>
<string name="first_time_skip">Ignorer le paramétrage</string>
<string name="first_time_blank_security_token">Voulez-vous utiliser ce jeton de sécurité vide avec OpenKeychain ?\n\nVeuillez retirer le jeton de sécurité maintenant, vous serez informé quand elle sera requis de nouveau !</string>
@@ -1385,7 +1396,7 @@
<string name="error_empty_text">Taper un texte à chiffrer !</string>
<string name="error_log_share_internal">Erreur interne durant la préparation du journal !</string>
<string name="key_colon">Clef :</string>
- <string name="exchange_description">Pour démarrer un échange de clef, choisir le nombre de participants du côté droit, puis cliquer sur le bouton « Démarrer l\'échange ».\n\Deux questions de plus seront posées pour s\'assurer que seuls les bons participants sont dans l\'échange et que les empreintes sont correctes.</string>
+ <string name="exchange_description">Pour démarrer un échange de clef, choisir le nombre de participants sur la droite, puis cliquer sur le bouton « Démarrer l\'échange ».\n\nDeux questions de plus vous seront posées pour garantir que seuls les bonnes personnes participent à l\'échange et que les empreintes sont correctes.</string>
<string name="btn_start_exchange">Démarrer l\'échange</string>
<string name="user_id_none"><![CDATA[<none>]]></string>
<!--Android Account-->
@@ -1424,12 +1435,12 @@
<string name="snack_security_token_import">Importer</string>
<string name="button_bind_key">Relier la clef</string>
<string name="security_token_serial_no">No de série : %s</string>
- <string name="security_token_key_holder">Détenteur de la clef :</string>
+ <string name="security_token_key_holder">Détenteur de la clef : %s</string>
<string name="security_token_key_holder_not_set"><![CDATA[Détenteur de la clef : <not set>]]></string>
<string name="security_token_status_bound">Le jeton de sécurité correspond et est relié à la clef</string>
<string name="security_token_status_unbound">Le jeton de sécurité correspond et peut être relié à la clef</string>
<string name="security_token_status_partly">Le jeton de sécurité correspond et est partiellement relié à la clef</string>
- <string name="security_token_create">Tenez le jeton de sécurité contre le dos de votre appareil.</string>
+ <string name="security_token_create">Tenir le jeton de sécurité contre le dos de votre appareil.</string>
<string name="security_token_reset_or_import">Ce jeton de sécurité contient déjà une clef. Vous pouvez importer la clef grâce au nuage, ou réinitialiser le jeton de sécurité.</string>
<string name="btn_import">Importer</string>
<string name="btn_reset">Réinitialiser</string>
@@ -1606,6 +1617,7 @@
<string name="help_donation_paypal_item">Don à OpenKeychain</string>
<string-array name="help_donation_google_catalog_values">
<item>1 EUR</item>
+ <item>2 EUR</item>
<item>3 EUR</item>
<item>5 EUR</item>
<item>10 EUR</item>
diff --git a/OpenKeychain/src/main/res/values-hi/strings.xml b/OpenKeychain/src/main/res/values-hi/strings.xml
index 677e5eefa..82c22b939 100644
--- a/OpenKeychain/src/main/res/values-hi/strings.xml
+++ b/OpenKeychain/src/main/res/values-hi/strings.xml
@@ -27,8 +27,6 @@
<!--InstallDialogFragment strings-->
<!--StartOrbotDialogFragment strings-->
<!--choice-->
- <!--key flags-->
- <string name="flag_encrypt">एन्क्रिप्ट</string>
<!--sentences-->
<!--errors
no punctuation, all lowercase,
diff --git a/OpenKeychain/src/main/res/values-hu/strings.xml b/OpenKeychain/src/main/res/values-hu/strings.xml
index b0e2fc1b8..86053fba1 100644
--- a/OpenKeychain/src/main/res/values-hu/strings.xml
+++ b/OpenKeychain/src/main/res/values-hu/strings.xml
@@ -15,7 +15,6 @@
<!--InstallDialogFragment strings-->
<!--StartOrbotDialogFragment strings-->
<!--choice-->
- <!--key flags-->
<!--sentences-->
<!--errors
no punctuation, all lowercase,
diff --git a/OpenKeychain/src/main/res/values-id/strings.xml b/OpenKeychain/src/main/res/values-id/strings.xml
index 664cfe628..7d16af500 100644
--- a/OpenKeychain/src/main/res/values-id/strings.xml
+++ b/OpenKeychain/src/main/res/values-id/strings.xml
@@ -30,8 +30,6 @@
<!--StartOrbotDialogFragment strings-->
<!--choice-->
<string name="choice_none">Tidak ada</string>
- <!--key flags-->
- <string name="flag_encrypt">Enkripsi</string>
<!--sentences-->
<!--errors
no punctuation, all lowercase,
diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml
index 94f9ce429..3d99314c9 100644
--- a/OpenKeychain/src/main/res/values-it/strings.xml
+++ b/OpenKeychain/src/main/res/values-it/strings.xml
@@ -8,6 +8,7 @@
<string name="title_encrypt_files">Codifica</string>
<string name="title_decrypt">Decodifica</string>
<string name="title_add_subkey">Aggiungi Sottochiave</string>
+ <string name="title_change_master_key">Cambia la chiave principale</string>
<string name="title_edit_key">Modifica Chiave</string>
<string name="title_linked_create">Creare un\'identità collegata</string>
<string name="title_preferences">Impostazioni</string>
@@ -30,6 +31,7 @@
<string name="title_help">Aiuto</string>
<string name="title_log_display">Registro</string>
<string name="title_exchange_keys">Scambia le chiavi</string>
+ <string name="title_advanced_key_info">Avanzato</string>
<string name="title_delete_secret_key">Cancellare la TUA chiave \'%s\'?</string>
<string name="title_manage_my_keys">Gestisci le mie chiavi</string>
<!--section-->
@@ -41,6 +43,7 @@
<string name="section_cloud_search_summary">Server chiavi, keybase.io</string>
<string name="section_passphrase_cache">Password e PIN</string>
<string name="section_proxy_settings">Rete anonimata</string>
+ <string name="section_proxy_settings_summary">Tor, Impostazioni del proxy</string>
<string name="section_gui">Interfaccia</string>
<string name="section_sync_settings">Sincronizzazione</string>
<string name="section_experimental_features">Caratteristiche sperimentali</string>
@@ -49,6 +52,7 @@
<string name="section_share_key">Chiave</string>
<string name="section_key_server">Server delle Chiavi</string>
<string name="section_fingerprint">Impronta</string>
+ <string name="section_phrases">Frasi</string>
<string name="section_encrypt">Codifica</string>
<string name="section_decrypt">Decodifica / verifica</string>
<string name="section_current_expiry">Scadenza attuale</string>
@@ -69,9 +73,16 @@
<string name="btn_back">Precedente</string>
<string name="btn_no">No</string>
<string name="btn_match">Impronte digitali ugali</string>
+ <string name="btn_match_phrases">Le frasi coincidono</string>
+ <string name="btn_share_encrypted_signed">Codifica/Firma e condividi testo</string>
+ <string name="btn_copy_encrypted_signed">Codifica/Firma e copia testo</string>
+ <string name="btn_paste_encrypted_signed">Codifica/Firma ed incolla testo</string>
<string name="btn_view_cert_key">Mostra chiave di certificazione</string>
<string name="btn_create_key">Crea chiave</string>
<string name="btn_add_files">Aggiungi file(s)</string>
+ <string name="btn_share_decrypted_text">Condividi</string>
+ <string name="btn_open_with">Apri con...</string>
+ <string name="btn_copy_decrypted_text">Copia negli appunti</string>
<string name="btn_decrypt_clipboard">Leggi dagli appunti</string>
<string name="btn_decrypt_files">Seleziona input file</string>
<string name="btn_encrypt_files">Codifica file</string>
@@ -81,7 +92,12 @@
<string name="btn_add_keyserver">Aggiungi</string>
<string name="btn_save_default">Salva come predefinito</string>
<string name="btn_saved">Salvato!</string>
+ <string name="btn_not_matching">Non corrispondono</string>
<!--Content Description-->
+ <string name="cd_encrypt_files">Codifica file</string>
+ <string name="cd_exchange_keys">Scambia le chiavi</string>
+ <string name="cd_encrypt_text">Cifra del testo semplice</string>
+ <string name="cd_share_nfc">Condividi via NFC</string>
<!--menu-->
<string name="menu_preferences">Impostazioni</string>
<string name="menu_help">Aiuto</string>
@@ -94,6 +110,8 @@
<string name="menu_select_all">Seleziona tutto</string>
<string name="menu_export_all_keys">Esporta tutte le chiavi</string>
<string name="menu_update_all_keys">Aggiorna tutte le chiavi</string>
+ <string name="menu_advanced">Avanzato</string>
+ <string name="menu_change_password">Cambia la password</string>
<string name="menu_keyserver_add">Aggiungi</string>
<!--label-->
<string name="label_message">Testo</string>
@@ -102,6 +120,7 @@
<string name="label_file_colon">File:</string>
<string name="label_no_passphrase">Nessuna password</string>
<string name="label_passphrase">Password</string>
+ <string name="label_pin">PIN</string>
<string name="label_unlock">Sbloccando...</string>
<string name="label_passphrase_again">Ripeti password</string>
<string name="label_show_passphrase">Mostra password</string>
@@ -121,6 +140,7 @@
<string name="label_keyservers">Seleziona server chiavi OpenPGP</string>
<string name="label_key_id">ID Chiave</string>
<string name="label_key_created">Chiave creata %s</string>
+ <string name="label_key_type">Tipo</string>
<string name="label_creation">Creazione</string>
<string name="label_expiry">Scadenza</string>
<string name="label_usage">Utilizzo</string>
@@ -139,29 +159,43 @@
<string name="label_enable_compression">Abilitare compressione</string>
<string name="label_encrypt_filenames">Codifica nome dei file</string>
<string name="label_hidden_recipients">Nascondi destinatari</string>
+ <string name="label_verify_keyserver_connection">Verifica la connessione</string>
+ <string name="label_only_trusted_keyserver">Solamente server delle chiavi fidati</string>
+ <string name="label_enter_keyserver_url">URL</string>
<string name="label_keyserver_dialog_delete">Cancella server chiavi</string>
+ <string name="label_theme">Tema</string>
<string name="pref_keyserver">Server chiavi OpenPGP</string>
<string name="pref_keyserver_summary">Cerca chiavi su server chiavi OpenPGP selezionati (protocollo HKP)</string>
<string name="pref_keybase">keybase.io</string>
<string name="pref_keybase_summary">Ricerca chiavi su keybase.io</string>
+ <string name="pref_facebook">Facebook</string>
+ <string name="label_sync_settings_keyserver_title">Aggiornamenti automatici delle chiavi</string>
+ <string name="label_sync_settings_wifi_title">Sincronizza solo via Wi-Fi</string>
<!--label shown in Android settings under the OpenKeychain account-->
+ <string name="keyserver_sync_settings_title">Aggiornamenti automatici delle chiavi</string>
<string name="label_experimental_settings_desc_title">Attenzione</string>
<!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Abilita Tor</string>
<string name="pref_proxy_type_title">Tipo proxy</string>
<!--proxy type choices and values-->
<string name="pref_proxy_type_choice_http">HTTP</string>
<string name="pref_proxy_type_choice_socks">SOCKS</string>
<!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Non usare Tor</string>
<!--InstallDialogFragment strings-->
<string name="orbot_install_dialog_title">Installa Orbot per usare Tor?</string>
<string name="orbot_install_dialog_install">Installa</string>
<string name="orbot_install_dialog_content">Orbot deve essere installato e attivato per fare attraversare il traffico dei dati tramite proxy. Vuoi installare Orbot?</string>
<string name="orbot_install_dialog_cancel">Annulla</string>
+ <string name="orbot_install_dialog_ignore_tor">Non usare Tor</string>
<!--StartOrbotDialogFragment strings-->
<string name="orbot_start_dialog_title">Attivare Orbot?</string>
<string name="orbot_start_btn">Attivare Orbot</string>
<string name="orbot_start_dialog_start">Attivare Orbot</string>
<string name="orbot_start_dialog_cancel">Annulla</string>
+ <string name="orbot_start_dialog_ignore_tor">Non usare Tor</string>
+ <string name="user_id_no_name"><![CDATA[<no name>]]></string>
+ <string name="none"><![CDATA[<none>]]></string>
<plurals name="n_keys">
<item quantity="one">1 chiave</item>
<item quantity="other">%d chiavi</item>
@@ -185,24 +219,26 @@
<string name="choice_4hours">4 ore</string>
<string name="choice_8hours">8 ore</string>
<string name="choice_forever">sempre</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
+ <string name="choice_select_cert">Seleziona una chiave</string>
<string name="filemanager_title_open">Apri...</string>
+ <string name="rsa_2048">RSA 2048</string>
+ <string name="rsa_3072">RSA 3072</string>
+ <string name="rsa_4096">RSA 4096</string>
+ <string name="ecc_p256">ECC P-256</string>
+ <string name="ecc_p521">ECC P-521</string>
+ <string name="usage_sign">Firma</string>
+ <string name="usage_encrypt">Codifica</string>
+ <string name="usage_sign_and_encrypt">Firma &amp; Codifica</string>
<string name="error">Errore</string>
<string name="error_message">Errore: %s</string>
- <!--key flags-->
- <string name="flag_certify">Certifica</string>
- <string name="flag_sign">Firma</string>
- <string name="flag_encrypt">Codifica</string>
- <string name="flag_authenticate">Convalida</string>
+ <string name="theme_dark">Scuro</string>
+ <string name="theme_light">Chiaro</string>
<!--sentences-->
<string name="wrong_passphrase">Password errata</string>
<string name="no_filemanager_installed">Nessun gestore file compatibile installato.</string>
<string name="passphrases_do_not_match">Le password non corrispondono.</string>
<string name="passphrase_must_not_be_empty">Si prega di inserire una password.</string>
+ <string name="passphrase_for_symmetric_encryption">Inserire password</string>
<string name="passphrase_for">Inserisci la password per \'%s\'</string>
<string name="pin_for">Inserisci il PIN per \'%s\'</string>
<string name="file_delete_confirmation_title">Eliminare i file originali?</string>
@@ -234,6 +270,7 @@
<string name="fingerprint_copied_to_clipboard">Impronta copiata negli appunti!</string>
<string name="select_key_to_certify">Per favore seleziona una chiave da utilizzare per la conferma!</string>
<string name="text_copied_to_clipboard">Il testo è stato copiato sulla lavagna!</string>
+ <string name="how_to_import">Come posso importarlo sul mio PC?</string>
<!--errors
no punctuation, all lowercase,
they will be put after "error_message", e.g. "Error: file not found"-->
@@ -270,6 +307,7 @@
<string name="decrypt_result_signature_missing_key">Firmato con chiave <b>pubblica sconusciuta</b></string>
<string name="decrypt_result_encrypted">Codificato</string>
<string name="decrypt_result_not_encrypted">Non Codificato</string>
+ <string name="decrypt_result_insecure">Cifratura non sicura</string>
<string name="decrypt_result_action_show">Mostra</string>
<string name="decrypt_invalid_button">Capisco i rischi, visualizza!</string>
<!--Add keys-->
@@ -280,6 +318,7 @@
<string name="progress_cancelling">cancellando...</string>
<string name="progress_saving">salvataggio...</string>
<string name="progress_importing">importazione...</string>
+ <string name="progress_benchmarking">verifica prestazioni del sistema...</string>
<string name="progress_updating">Aggiorna chiavi...</string>
<string name="progress_exporting">esportazione...</string>
<string name="progress_uploading">caricamento...</string>
@@ -325,6 +364,8 @@
<string name="progress_deleting">cancellazione chiavi...</string>
<string name="progress_con_saving">consolidazione: salvataggio della cache...</string>
<string name="progress_con_reimport">consolidazione: reimportazione...</string>
+ <string name="progress_verifying_keyserver_connection">verifica della connessione...</string>
+ <string name="progress_starting_orbot">Avvio di Orbot in corso...</string>
<!--action strings-->
<!--key bit length selections-->
<string name="key_size_2048">2048</string>
@@ -354,6 +395,7 @@
<string name="help_tab_changelog">Novita\'</string>
<string name="help_tab_about">Info</string>
<string name="help_about_version">Versione:</string>
+ <string name="help_tab_donations">Fai una donazione</string>
<!--Import-->
<string name="import_tab_keyserver">Server delle chiavi</string>
<string name="import_tab_direct">File/Appunti</string>
@@ -406,6 +448,7 @@
<string name="api_settings_start">Avvia applicazione</string>
<string name="api_settings_delete_account">Cancella account</string>
<string name="api_settings_package_name">Nome Pacchetto</string>
+ <string name="api_settings_advanced">Avanzato</string>
<string name="api_settings_settings">Impostazioni</string>
<string name="api_settings_key">Chiave account:</string>
<string name="api_settings_accounts_empty">Nessun account collegato a questa applicazione</string>
@@ -423,6 +466,7 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="share_qr_code_dialog_title">Condividi tramite Codice QR</string>
<string name="share_nfc_dialog">Condividi tramite NFC</string>
<!--retry upload dialog-->
+ <string name="retry_up_dialog_btn_cancel">Annulla operazione</string>
<!--Delete or revoke private key dialog-->
<!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
@@ -432,7 +476,10 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
</plurals>
<string name="key_list_empty_text1">Nessuna chiave trovata!</string>
<string name="key_list_filter_show_all">Mostra tutte le chiavi</string>
+ <string name="key_list_fab_qr_code">Scansione codice QR</string>
+ <string name="key_list_fab_import">Importa da file</string>
<!--Key view-->
+ <string name="key_view_action_edit">Modifica</string>
<string name="key_view_action_encrypt">Codifica Testo</string>
<string name="key_view_action_encrypt_files">documenti</string>
<string name="key_view_action_update">Aggiorna dal server delle chiavi</string>
@@ -440,16 +487,24 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="key_view_action_share_nfc">Condividi tramite NFC</string>
<string name="key_view_action_upload">Carica sul Server delle Chiavi</string>
<string name="key_view_tab_main">Info Principale</string>
+ <string name="key_view_tab_start">Inizia</string>
<string name="key_view_tab_share">Condividi</string>
<string name="key_view_tab_keys">Sottochiavi</string>
<string name="key_view_tab_certs">Certificati</string>
+ <string name="key_view_tab_keybase">Keybase.io</string>
<string name="user_id_info_revoked_title">Revocato</string>
<string name="user_id_info_revoked_text">Questa identità è stata revocata dal suo proprietario. Non è più valida.</string>
+ <string name="user_id_info_uncertified_title">Non confermata</string>
<string name="user_id_info_invalid_title">Non valido</string>
<string name="user_id_info_invalid_text">C\'è qualcosa che non va con questa identità!</string>
<!--Key trust-->
<!--keybase proof stuff-->
+ <string name="keybase_problem_fetching_evidence">Errore durante la verifica</string>
+ <string name="keybase_twitter_proof">Un tweet</string>
+ <string name="keybase_web_site_proof">Un file di testo</string>
+ <string name="keybase_verify">Verifica</string>
<!--Edit key-->
+ <string name="edit_key_action_change_passphrase">Cambio password</string>
<string name="edit_key_action_add_identity">Aggiungi Identità</string>
<string name="edit_key_action_add_subkey">Aggiungi Sottochiave</string>
<string name="edit_key_edit_user_id_title">Seleziona un azione!</string>
@@ -463,18 +518,27 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="edit_key_edit_user_id_revoked">Questa identità è stata revocata. Ciò non può essere annullato.</string>
<string name="edit_key_edit_subkey_title">Seleziona un azione!</string>
<string name="edit_key_new_subkey">nuova sottochiave</string>
- <string name="edit_key_select_flag">Per favore seleziona almeno una spunta!</string>
<string name="edit_key_error_add_identity">Aggiungi almeno una identità</string>
<string name="edit_key_error_add_subkey">Aggiungi almeno una sottochiave!</string>
<!--Create key-->
<string name="create_key_empty">Questo campo è necessario</string>
+ <string name="create_key_passphrases_not_equal">Le password non sono identiche</string>
<string name="create_key_final_text">Hai inserito la seguente identità:</string>
<string name="create_key_final_robot_text">La creazione di una chiave richiede un po\' di tempo, prendi un caffè nel frattempo...</string>
<string name="create_key_rsa">(3 sottochiavi, RSA, 4096 bit)</string>
<string name="create_key_custom">(personalizza la configurazione della chiave)</string>
+ <string name="create_key_hint_full_name">Nome o nick</string>
<string name="create_key_edit">Cambia configurazione della chiave</string>
+ <string name="create_key_add_email">Aggiungi indirizzo email</string>
+ <string name="create_key_yubi_key_pin">PIN</string>
+ <string name="create_key_yubi_key_pin_repeat">Ripeti il PIN</string>
+ <string name="create_key_yubi_key_pin_not_correct">Il PIN è errato!</string>
+ <string name="create_key_yubi_key_pin_too_short">Il PIN dovrebbe essere composto almeno da 6 numeri.</string>
<!--View key-->
<!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Aggiungi server delle chiavi</string>
+ <string name="add_keyserver_connection_verified">Connessione verificata.</string>
+ <string name="add_keyserver_invalid_url">URL non valido!</string>
<!--Navigation Drawer-->
<string name="nav_keys">Chiavi</string>
<string name="nav_apps">Apps</string>
@@ -628,6 +692,7 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="msg_mf_error_integrity">Errore interno, controllo di integrità fallito!</string>
<string name="msg_mf_error_master_none">Nessun certificato principale trovato su cui operare! (Tutti revocati?)</string>
<string name="msg_mf_error_null_expiry">La data di scadenza non può essere \"come prima\" sulla creazione di sottochiavi. Questo è un errore di programmazione, si prega di inviare una segnalazione di bug!</string>
+ <string name="msg_mf_error_noop">Non c\'è nessuna azione da eseguire.</string>
<string name="msg_mf_error_passphrase_master">Errore irreversibile nella decodifica della chiave principale! Questo è probabilmente un errore di programmazione, si prega di inviare una segnalazione di bug!</string>
<string name="msg_mf_error_sig">Eccezione di firma!</string>
<string name="msg_mf_error_subkey_missing">Tentativo di operare su sottochiave %s mancante!</string>
@@ -671,7 +736,9 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="msg_con_warn_delete_public">Eccezione durante la eliminazione del file di cache pubblico</string>
<string name="msg_con_warn_delete_secret">Eccezione durante la eliminazione del file di cache privato</string>
<!--Edit Key (higher level than modify)-->
+ <string name="msg_ed_error_key_not_found">Chiave non trovata.</string>
<!--Promote key-->
+ <string name="msg_pr_error_key_not_found">Chiave non trovata.</string>
<!--Other messages used in OperationLogs-->
<string name="msg_ek_error_not_found">Chiave non trovata!</string>
<!--Messages for DecryptVerify operation-->
@@ -687,18 +754,29 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="msg_dc_sym_skip">Dati simmetrici non permessi, proseguo...</string>
<string name="msg_dc_unlocking">Sblocco chiave segreta</string>
<!--Messages for VerifySignedLiteralData operation-->
+ <string name="msg_vl_clear_meta_file">Nome file: %s</string>
+ <string name="msg_vl_clear_meta_mime">Tipo MIME: %s</string>
+ <string name="msg_vl_clear_meta_time">Ultima modifica: %s</string>
+ <string name="msg_vl_clear_meta_size">Dimesione file: %s</string>
+ <string name="msg_vl_ok">OK</string>
<!--Messages for SignEncrypt operation-->
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_crt_success">Identità certificata correttamente</string>
<string name="msg_crt_warn_not_found">Chiave non trovata!</string>
<string name="msg_crt_upload_success">Chiave caricata con successo sul server</string>
+ <string name="msg_upload_server">Server: %s</string>
<string name="msg_del_error_empty">Niente da cancellare!</string>
<!--Linked Identity verification-->
+ <string name="msg_data_mime_filename">Nome file: \'%s\'</string>
<string name="msg_acc_saved">Account salvato</string>
<!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_error_specific">%s</string>
<!--Messages for Mime parsing operation-->
<!--PassphraseCache-->
+ <string name="passp_cache_notif_pwd">Password</string>
<!--Keyserver sync-->
+ <string name="keyserver_sync_orbot_notif_msg">Fai click per avviare Orbot</string>
+ <string name="keyserver_sync_orbot_notif_start">Avvia Orbot</string>
<!--First Time-->
<string name="first_time_text1">Riappropriati della tua privacy con OpenKeychain!</string>
<string name="first_time_skip">Salta Installazione</string>
@@ -706,6 +784,7 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="section_certifier_id">Certificatore</string>
<string name="section_cert">Dettagli Certificato</string>
<string name="label_user_id">Identit</string>
+ <string name="unknown_uid"><![CDATA[<unknown>]]></string>
<string name="empty_certs">Nessun certificato per questa chiave</string>
<string name="label_revocation">Ragione della Revoca</string>
<string name="label_cert_type">Tipo</string>
@@ -721,11 +800,49 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="contact_show_key">Mostra chiave (%s)</string>
<string name="error_no_file_selected">Seleziona almeno un file da codificare!</string>
<string name="key_colon">Chiave:</string>
+ <string name="user_id_none"><![CDATA[<none>]]></string>
<!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <string name="passphrase">Password</string>
+ <string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
+ <string name="security_token_key_holder_not_set"><![CDATA[Key holder: <not set>]]></string>
+ <string name="security_token_error">Errore: %s</string>
+ <string name="security_token_error_unknown">Errore sconosciuto</string>
+ <string name="security_token_error_try_again">Riprovare</string>
<string name="file_saved">File salvato!</string>
<!--Other Linked Identity strings-->
+ <string name="linked_verifying">Verifica in corso...</string>
+ <string name="linked_verify_success">Verificato!</string>
+ <string name="linked_title_https">Sito (HTTPS)</string>
+ <string name="linked_title_github">GitHub</string>
+ <string name="linked_title_twitter">Twitter</string>
+ <string name="linked_text_error">Errore</string>
+ <string name="share_log_dialog_cancel_button">Cancella</string>
+ <string name="menu_uids_save">Salva</string>
+ <string name="cache_ttl_ten_minutes">per dieci minuti</string>
+ <string name="cache_ttl_thirty_minutes">per mezz\'ora</string>
+ <string name="cache_ttl_one_hour">per un\'ora</string>
+ <string name="cache_ttl_three_hours">per tre ore</string>
+ <string name="cache_ttl_one_day">per una giornata</string>
+ <string name="cache_ttl_three_days">per tre giorni</string>
+ <string name="cache_ttl_forever">per sempre</string>
+ <string name="prompt_fidesmo_pgp_install_title">Installare PGP?</string>
+ <string name="prompt_fidesmo_pgp_install_button_positive">Installa</string>
+ <string name="prompt_fidesmo_pgp_install_button_negative">Annulla</string>
+ <string name="prompt_fidesmo_app_install_title">Installare Fidesmo?</string>
+ <string name="prompt_fidesmo_app_install_button_positive">Installa</string>
+ <string name="prompt_fidesmo_app_install_button_negative">Annulla</string>
+ <string name="help_donation_paypal_item">Donazione ad OpenKeychain</string>
+ <string-array name="help_donation_google_catalog_values">
+ <item>1 EUR</item>
+ <item>2 EUR</item>
+ <item>3 EUR</item>
+ <item>5 EUR</item>
+ <item>10 EUR</item>
+ <item>50 EUR</item>
+ <item>100 EUR</item>
+ </string-array>
</resources>
diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml
index 80fed8316..d5324c2b4 100644
--- a/OpenKeychain/src/main/res/values-ja/strings.xml
+++ b/OpenKeychain/src/main/res/values-ja/strings.xml
@@ -8,6 +8,7 @@
<string name="title_encrypt_files">暗号化</string>
<string name="title_decrypt">復号化</string>
<string name="title_add_subkey">副鍵の追加</string>
+ <string name="title_change_master_key">主鍵の変更</string>
<string name="title_edit_key">鍵の編集</string>
<string name="title_linked_create">リンクしたユーザIDを作成</string>
<string name="title_preferences">設定</string>
@@ -35,6 +36,7 @@
<string name="title_advanced_key_info">詳細</string>
<string name="title_delete_secret_key">あなたの鍵 \'%s\' を削除しますか?</string>
<string name="title_manage_my_keys">自分の鍵の管理</string>
+ <string name="title_alert_strip">副鍵のストリップ</string>
<!--section-->
<string name="section_user_ids">ユーザID</string>
<string name="section_security_token">セキュリティ トークン</string>
@@ -154,6 +156,7 @@
<string name="label_keyservers">OpenPGP鍵サーバを選択</string>
<string name="label_key_id">鍵ID</string>
<string name="label_key_created">%s で鍵を生成</string>
+ <string name="label_key_type">種別</string>
<string name="label_creation">生成</string>
<string name="label_expiry">満了</string>
<string name="label_usage">使い方</string>
@@ -187,6 +190,7 @@
<string name="label_sync_settings_keyserver_title">鍵の自動アップデート</string>
<string name="label_sync_settings_keyserver_summary_on">3日ごとに鍵サーバから鍵をアップデートします</string>
<string name="label_sync_settings_keyserver_summary_off">鍵を自動でアップデートしません</string>
+ <string name="label_sync_settings_wifi_title">Wi-Fiでのみ同期</string>
<string name="label_sync_settings_contacts_title">連絡先へ鍵をリンク</string>
<string name="label_sync_settings_contacts_summary_on">オフランで完結して、名前とメールアドレスに基づいて、鍵を連絡先にリンク</string>
<string name="label_sync_settings_contacts_summary_off">新しい鍵は連絡先と関連付けしない</string>
@@ -252,21 +256,26 @@
<string name="choice_8hours">8時間</string>
<string name="choice_forever">永遠</string>
<string name="choice_select_cert">鍵を選択</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">開く...</string>
+ <string name="rsa_2048">RSA 2048</string>
+ <string name="rsa_2048_description_html">小さいファイルサイズ。2030年までは安全であると考えられます</string>
+ <string name="rsa_3072">RSA 3072</string>
+ <string name="rsa_3072_description_html">推奨。2040年までは安全であると考えられます</string>
+ <string name="rsa_4096">RSA 4096</string>
+ <string name="rsa_4096_description_html">大きなファイルサイズ。2040年以降も安全であると考えられます</string>
+ <string name="ecc_p256">ECC P-256</string>
+ <string name="ecc_p256_description_html">極めて小さいファイルサイズ。2040年までは安全であると考えられます &lt;br/&gt; &lt;u&gt;実験的で、すべての実装でサポートされるわけではありません&lt;/u&gt;</string>
+ <string name="ecc_p521">ECC P-521</string>
+ <string name="ecc_p521_description_html">非常に小さいファイルサイズ。2040年以降も安全であると考えられます &lt;br/&gt; &lt;u&gt;実験的で、すべての実装でサポートされるわけではありません&lt;/u&gt;</string>
+ <string name="usage_none">なし (副鍵バインディングのみ)</string>
+ <string name="usage_sign">署名</string>
+ <string name="usage_encrypt">暗号化</string>
+ <string name="usage_sign_and_encrypt">署名と暗号化</string>
<string name="error">エラー</string>
<string name="error_message">エラー: %s</string>
<string name="theme_dark">ダーク</string>
<string name="theme_light">ライト</string>
- <!--key flags-->
- <string name="flag_certify">証明</string>
- <string name="flag_sign">署名</string>
- <string name="flag_encrypt">暗号化</string>
- <string name="flag_authenticate">認証</string>
+ <string name="strip">ストリップ</string>
<!--sentences-->
<string name="wrong_passphrase">正しくないパスワードです。</string>
<string name="no_filemanager_installed">互換性のないファイルマネージャがインストールされています。</string>
@@ -300,6 +309,7 @@
<string name="public_key_deletetion_confirmation">鍵 \'%s\' を削除しますか?</string>
<string name="also_export_secret_keys">秘密鍵もエクスポートします</string>
<string name="reinstall_openkeychain">あなたは既知のAndroidのバグに遭遇しました。もし鍵とあなたの連絡先をリンクさせたいならOpenKeychainを再インストールしてください。</string>
+ <string name="alert_strip">この副鍵をストリップすると、このデバイスで使用不能になってしまいます!</string>
<string name="key_exported">1つの鍵をエクスポートしました。</string>
<string name="keys_exported">%d の鍵をエクスポートしました。</string>
<string name="no_keys_exported">鍵をエクスポートしていません。</string>
@@ -325,6 +335,7 @@
<string name="error_file_delete_failed">まだ削除されていません。手動での削除です!</string>
<string name="error_file_added_already">%s はすでに追加済みです。</string>
<string name="error_file_not_found">ファイルが見付かりません</string>
+ <string name="error_bad_data">データ不良!</string>
<string name="error_no_secret_key_found">組になっている秘密鍵が見付かりません</string>
<string name="error_external_storage_not_ready">外部ストレージが準備できていません</string>
<string name="error_key_size_minimum512bit">鍵サイズは最低でも512bit必要です</string>
@@ -666,7 +677,7 @@
<item>副鍵をセキュリティトークンへ移動</item>
</string-array>
<string name="edit_key_new_subkey">新しい副鍵</string>
- <string name="edit_key_select_flag">最低1つフラグを選択してください!</string>
+ <string name="edit_key_select_usage">使用する鍵を選択してください!</string>
<string name="edit_key_error_add_identity">最低でも1つのユーザIDを追加!</string>
<string name="edit_key_error_add_subkey">最低でも1つの副鍵を追加!</string>
<string name="edit_key_error_bad_security_token_algo">セキュリティトークンでサポートされないアルゴリズムです!</string>
@@ -1400,7 +1411,7 @@
<string name="snack_security_token_import">インポート</string>
<string name="button_bind_key">鍵と紐付け</string>
<string name="security_token_serial_no">シリアルNo: %s</string>
- <string name="security_token_key_holder">鍵ホルダ:</string>
+ <string name="security_token_key_holder">鍵ホルダ: %s</string>
<string name="security_token_key_holder_not_set"><![CDATA[鍵ホルダ: <未設定>]]></string>
<string name="security_token_status_bound">セキュリティトークンが一致、鍵に紐付けます</string>
<string name="security_token_status_unbound">セキュリティトークンが一致、鍵に紐付けることができます</string>
@@ -1580,6 +1591,7 @@
<string name="help_donation_paypal_item">OpenKeychain の寄付</string>
<string-array name="help_donation_google_catalog_values">
<item>1 ユーロ</item>
+ <item>2 ユーロ</item>
<item>3 ユーロ</item>
<item>5 ユーロ</item>
<item>10 ユーロ</item>
diff --git a/OpenKeychain/src/main/res/values-kn/strings.xml b/OpenKeychain/src/main/res/values-kn/strings.xml
index b0e2fc1b8..86053fba1 100644
--- a/OpenKeychain/src/main/res/values-kn/strings.xml
+++ b/OpenKeychain/src/main/res/values-kn/strings.xml
@@ -15,7 +15,6 @@
<!--InstallDialogFragment strings-->
<!--StartOrbotDialogFragment strings-->
<!--choice-->
- <!--key flags-->
<!--sentences-->
<!--errors
no punctuation, all lowercase,
diff --git a/OpenKeychain/src/main/res/values-ko/strings.xml b/OpenKeychain/src/main/res/values-ko/strings.xml
index 56924b973..7a920db40 100644
--- a/OpenKeychain/src/main/res/values-ko/strings.xml
+++ b/OpenKeychain/src/main/res/values-ko/strings.xml
@@ -225,21 +225,11 @@
<string name="choice_8hours">8시간</string>
<string name="choice_forever">영원히</string>
<string name="choice_select_cert">키를 선택하세요</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">열기</string>
<string name="error">에러</string>
<string name="error_message">에러: %s</string>
<string name="theme_dark">어두움</string>
<string name="theme_light">밝음</string>
- <!--key flags-->
- <string name="flag_certify">확인</string>
- <string name="flag_sign">서명</string>
- <string name="flag_encrypt">암호화</string>
- <string name="flag_authenticate">인증</string>
<!--sentences-->
<string name="wrong_passphrase">암호가 틀렸습니다.</string>
<string name="no_filemanager_installed">호환되는 파일 관리자가 설치되어 있지 않음.</string>
diff --git a/OpenKeychain/src/main/res/values-la/strings.xml b/OpenKeychain/src/main/res/values-la/strings.xml
new file mode 100644
index 000000000..86053fba1
--- /dev/null
+++ b/OpenKeychain/src/main/res/values-la/strings.xml
@@ -0,0 +1,87 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--GENERAL: Please put all strings inside quotes as described in example 1 on
+ http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
+ <!--title-->
+ <!--section-->
+ <!--button-->
+ <!--Content Description-->
+ <!--menu-->
+ <!--label-->
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
+ <!--choice-->
+ <!--sentences-->
+ <!--errors
+ no punctuation, all lowercase,
+ they will be put after "error_message", e.g. "Error: file not found"-->
+ <!--errors without preceeding Error:-->
+ <!--results shown after decryption/verification-->
+ <!--Add keys-->
+ <!--progress dialogs, usually ending in '…'-->
+ <!--action strings-->
+ <!--key bit length selections-->
+ <!--elliptic curve names-->
+ <!--not in for now, see SaveKeyringParcel
+ <string name="key_curve_bp_p256">"Brainpool P-256"</string>
+ <string name="key_curve_bp_p384">"Brainpool P-384"</string>
+ <string name="key_curve_bp_p512">"Brainpool P-512"</string>-->
+ <!--compression-->
+ <!--Help-->
+ <!--Import-->
+ <!--Import from URL-->
+ <!--Generic result toast-->
+ <!--Import result toast-->
+ <!--Delete result toast-->
+ <!--Revoke result toast (snackbar)-->
+ <!--Certify result toast-->
+ <!--Intent labels-->
+ <!--Remote API-->
+ <!--Share-->
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
+ <!--Key list-->
+ <!--Key view-->
+ <!--Key trust-->
+ <!--keybase proof stuff-->
+ <!--Edit key-->
+ <!--Create key-->
+ <!--View key-->
+ <!--Add/Edit keyserver-->
+ <!--Navigation Drawer-->
+ <!--hints-->
+ <!--certs-->
+ <!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
+ <!--Import Public log entries-->
+ <!--Import Secret log entries-->
+ <!--Keyring Canonicalization log entries-->
+ <!--Keyring merging log entries-->
+ <!--createSecretKeyRing-->
+ <!--modifySecretKeyRing-->
+ <!--Consolidate-->
+ <!--Edit Key (higher level than modify)-->
+ <!--Promote key-->
+ <!--Other messages used in OperationLogs-->
+ <!--Messages for DecryptVerify operation-->
+ <!--Messages for VerifySignedLiteralData operation-->
+ <!--Messages for SignEncrypt operation-->
+ <!--Messages for PgpSignEncrypt operation-->
+ <!--Linked Identity verification-->
+ <!--Messages for Keybase Verification operation-->
+ <!--Messages for Mime parsing operation-->
+ <!--PassphraseCache-->
+ <!--Keyserver sync-->
+ <!--First Time-->
+ <!--unsorted-->
+ <!--Android Account-->
+ <!--Passphrase wizard-->
+ <!--TODO: rename all the things!-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
+ <!--Other Linked Identity strings-->
+</resources>
diff --git a/OpenKeychain/src/main/res/values-nb/strings.xml b/OpenKeychain/src/main/res/values-nb/strings.xml
index b0e2fc1b8..b90ac76ad 100644
--- a/OpenKeychain/src/main/res/values-nb/strings.xml
+++ b/OpenKeychain/src/main/res/values-nb/strings.xml
@@ -2,9 +2,85 @@
<resources>
<!--GENERAL: Please put all strings inside quotes as described in example 1 on
http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
+ <string name="app_name">OpenKeychain</string>
<!--title-->
+ <string name="title_encrypt_text">Krypter</string>
+ <string name="title_encrypt_files">Krypter</string>
+ <string name="title_decrypt">Dekrypter</string>
+ <string name="title_add_subkey">Legg til undernøkkel</string>
+ <string name="title_change_master_key">Endre hovednøkkel</string>
+ <string name="title_edit_key">Rediger nøkkel</string>
+ <string name="title_linked_create">Lag en tilknyttet identitet</string>
+ <string name="title_preferences">Innstillinger</string>
+ <string name="title_api_registered_apps">Programmer</string>
+ <string name="title_key_server_preference">OpenPGP-nøkkeltjenere</string>
+ <string name="title_cache_ttl_preference">Tilpass \"husk\"-valg</string>
+ <string name="title_change_passphrase">Endre passord</string>
+ <string name="title_share_fingerprint_with">Del fingeravtrykk med…</string>
+ <string name="title_share_key">Del nøkkel med…</string>
+ <string name="title_share_file">Del fil med…</string>
+ <string name="title_share_message">Del tekst med…</string>
+ <string name="title_encrypt_to_file">Krypter til fil</string>
+ <string name="title_decrypt_to_file">Dekrypter til fil</string>
+ <string name="title_import_keys">Importer nøkler</string>
+ <string name="title_export_key">Sikkerhetskopier nøkkel</string>
+ <string name="title_export_keys">Sikkerhetskopier nøkler</string>
+ <string name="title_key_not_found">Finner ikke nøkkel</string>
+ <string name="title_send_key">Last opp til nøkkeltjener</string>
+ <string name="title_backup">Sikkerhetskopi</string>
+ <string name="title_certify_key">Bekreft nøkkel</string>
+ <string name="title_key_details">Nøkkeldetaljer</string>
+ <string name="title_help">Hjelp</string>
+ <string name="title_log_display">Logg</string>
+ <string name="title_exchange_keys">Utveksle nøkler</string>
+ <string name="title_advanced_key_info">Avansert</string>
+ <string name="title_delete_secret_key">Slett DIN nøkkel \"%s\"?</string>
+ <string name="title_manage_my_keys">Behandle nøklene mine</string>
+ <string name="title_alert_strip">Fjern denne undernøkkelen</string>
<!--section-->
+ <string name="section_user_ids">Identiteter</string>
+ <string name="section_security_token">Sikkerhetssymbol</string>
+ <string name="section_linked_system_contact">Lenket systemkontakt</string>
+ <string name="section_keybase_proofs">Keybase.io-beviser</string>
+ <string name="section_should_you_trust">Kan du stole på denne nøkkelen?</string>
+ <string name="section_proof_details">Bekreft bevis</string>
+ <string name="section_keys">Undernøkler</string>
+ <string name="section_cloud_search">Finn nøkkel</string>
+ <string name="section_cloud_search_summary">Nøkkeltjener, keybase.io</string>
+ <string name="section_passphrase_cache">Passord og PIN-koder</string>
+ <string name="section_proxy_settings">Nettverks-anonymitet</string>
+ <string name="section_proxy_settings_summary">Tor, mellomtjener-innstillinger</string>
+ <string name="section_gui">Grensesnitt</string>
+ <string name="section_sync_settings">Synkronisering</string>
+ <string name="section_sync_settings_summary">Automatisk nøkkel-oppdatering, kontakt-lenking</string>
+ <string name="section_experimental_features">Eksperimentelle funksjoner</string>
+ <string name="section_certify">Bekreft</string>
+ <string name="section_actions">Handlinger</string>
+ <string name="section_share_key">Nøkkel</string>
+ <string name="section_key_server">Nøkkeltjener</string>
+ <string name="section_fingerprint">Fingeravtrykk</string>
+ <string name="section_phrases">Fraser</string>
+ <string name="section_encrypt">Krypter</string>
+ <string name="section_decrypt">Dekrypter / bekreft</string>
+ <string name="section_current_expiry">Gjeldende forfall</string>
+ <string name="section_new_expiry">Nytt forfall</string>
<!--button-->
+ <string name="btn_decrypt_verify_file">Dekrypter, verifiser, og lagre fil</string>
+ <string name="btn_encrypt_share_file">Krypter og del fil</string>
+ <string name="btn_encrypt_save_file">Krypter og lagre fil</string>
+ <string name="btn_save_file">Lagre fil</string>
+ <string name="btn_save">Lagre</string>
+ <string name="btn_view_log">Se logg</string>
+ <string name="btn_do_not_save">Avbryt</string>
+ <string name="btn_delete">Slett</string>
+ <string name="btn_no_date">Ingen forfall</string>
+ <string name="btn_okay">Ok</string>
+ <string name="btn_export_to_server">Last opp til nøkkelserver</string>
+ <string name="btn_next">Neste</string>
+ <string name="btn_back">Tilbake</string>
+ <string name="btn_no">Nei</string>
+ <string name="btn_share_encrypted_signed">Krypter/signer og del tekst</string>
+ <string name="btn_copy_encrypted_signed">Krypter/signer og kopier tekst</string>
<!--Content Description-->
<!--menu-->
<!--label-->
@@ -14,9 +90,48 @@
<!--OrbotHelper strings-->
<!--InstallDialogFragment strings-->
<!--StartOrbotDialogFragment strings-->
+ <string name="secret_key">Hemmelig nøkkel:</string>
<!--choice-->
- <!--key flags-->
+ <string name="choice_none">Ingen</string>
+ <string name="choice_15secs">15 sekunder</string>
+ <string name="choice_1min">ett min</string>
+ <string name="choice_3mins">tre min</string>
+ <string name="choice_5mins">fem min</string>
+ <string name="choice_10mins">ti min</string>
+ <string name="choice_20mins">20 min</string>
+ <string name="choice_40mins">40 min</string>
+ <string name="choice_1hour">én time</string>
+ <string name="choice_2hours">to timer</string>
+ <string name="choice_4hours">fire timer</string>
+ <string name="choice_8hours">åtte timer</string>
+ <string name="choice_forever">for all tid</string>
+ <string name="choice_select_cert">Velg en nøkkel</string>
+ <string name="filemanager_title_open">Åpne…</string>
+ <string name="rsa_2048">RSA 2048</string>
+ <string name="rsa_2048_description_html">mindre filstørrelse, antatt sikker til 2030</string>
+ <string name="rsa_3072">RSA 3072</string>
+ <string name="rsa_3072_description_html">anbefalt, antatt sikker til 2040</string>
+ <string name="rsa_4096">RSA 4096</string>
+ <string name="rsa_4096_description_html">større filstørrelse, antatt sikker til 2040+</string>
+ <string name="ecc_p256">ECC P-256</string>
+ <string name="ecc_p521">ECC P-521</string>
+ <string name="usage_sign">Signer</string>
+ <string name="usage_encrypt">Krypter</string>
+ <string name="usage_sign_and_encrypt">Signer &amp; krypter</string>
+ <string name="error">Feil</string>
+ <string name="error_message">Feil: %s</string>
+ <string name="theme_dark">Mørkt</string>
+ <string name="theme_light">Lyst</string>
<!--sentences-->
+ <string name="wrong_passphrase">Feil passord.</string>
+ <string name="no_filemanager_installed">Ingen kompatibel filbehandler installert.</string>
+ <string name="passphrases_do_not_match">Passordene samsvarer ikke.</string>
+ <string name="passphrase_must_not_be_empty">Skriv inn ett passord.</string>
+ <string name="passphrase_for_symmetric_encryption">Skriv inn passord</string>
+ <string name="passphrase_for_backup">Skriv inn sikkerhetskopierings-kode</string>
+ <string name="passphrase_for">Skriv inn passord fo \'%s\'</string>
+ <string name="pin_for">Skriv inn PIN for \'%s\'</string>
+ <string name="security_token_pin_for">Skriv inn PIN for å få tilgang til sikkerhetssymbol for \'%s\'</string>
<!--errors
no punctuation, all lowercase,
they will be put after "error_message", e.g. "Error: file not found"-->
diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml
index 0e9f2e3bd..723c312a5 100644
--- a/OpenKeychain/src/main/res/values-nl/strings.xml
+++ b/OpenKeychain/src/main/res/values-nl/strings.xml
@@ -185,19 +185,9 @@
<string name="choice_4hours">4 uur</string>
<string name="choice_8hours">8 uur</string>
<string name="choice_forever">voor altijd</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Openen…</string>
<string name="error">Fout</string>
<string name="error_message">Fout: %s</string>
- <!--key flags-->
- <string name="flag_certify">Certificeer</string>
- <string name="flag_sign">Ondertekenen</string>
- <string name="flag_encrypt">Versleutelen</string>
- <string name="flag_authenticate">Legitimeren</string>
<!--sentences-->
<string name="wrong_passphrase">Wachtwoord verkeerd.</string>
<string name="no_filemanager_installed">Geen compatibele bestandsbeheerder geïnstalleerd.</string>
@@ -528,7 +518,6 @@
<string name="edit_key_edit_user_id_revoked">Deze identiteit is ingetrokken. Dit kan niet ongedaan gemaakt worden.</string>
<string name="edit_key_edit_subkey_title">Selecteer een actie!</string>
<string name="edit_key_new_subkey">nieuwe subsleutel</string>
- <string name="edit_key_select_flag">Gelieve minstens een vlag te selecteren!</string>
<string name="edit_key_error_add_identity">Voeg minstens een identiteit toe!</string>
<string name="edit_key_error_add_subkey">Voeg minstens een subsleutel toe!</string>
<!--Create key-->
diff --git a/OpenKeychain/src/main/res/values-pl/strings.xml b/OpenKeychain/src/main/res/values-pl/strings.xml
index 0de3a09ca..e58ba1576 100644
--- a/OpenKeychain/src/main/res/values-pl/strings.xml
+++ b/OpenKeychain/src/main/res/values-pl/strings.xml
@@ -119,19 +119,9 @@
<string name="choice_4hours">4 godziny</string>
<string name="choice_8hours">8 godzin</string>
<string name="choice_forever">na zawsze</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Otwórz...</string>
<string name="error">Błąd</string>
<string name="error_message">Błąd: %s</string>
- <!--key flags-->
- <string name="flag_certify">Certyfikuj</string>
- <string name="flag_sign">Podpisuj</string>
- <string name="flag_encrypt">Szyfruj</string>
- <string name="flag_authenticate">Uwierzytelniaj</string>
<!--sentences-->
<string name="no_filemanager_installed">Nie zainstalowano żadnego kompatybilnego menadżera plików.</string>
<string name="pin_for">Wpisz PIN dla \'%s\'</string>
@@ -401,7 +391,6 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
<string name="edit_key_edit_user_id_revoked">Ta tożsamość została unieważniona. Tego już nie można cofnąć.</string>
<string name="edit_key_edit_subkey_title">Wybierz zadanie!</string>
<string name="edit_key_new_subkey">nowy pod-klucz</string>
- <string name="edit_key_select_flag">Prosimy o wybranie przynajmniej jeden flagi!</string>
<string name="edit_key_error_add_identity">Dodaj przynajmniej jedną tożsamość!</string>
<string name="edit_key_error_add_subkey">Dodaj przynajmniej jeden pod-klucz!</string>
<!--Create key-->
diff --git a/OpenKeychain/src/main/res/values-pt-rBR/strings.xml b/OpenKeychain/src/main/res/values-pt-rBR/strings.xml
index b0e2fc1b8..472e4cacf 100644
--- a/OpenKeychain/src/main/res/values-pt-rBR/strings.xml
+++ b/OpenKeychain/src/main/res/values-pt-rBR/strings.xml
@@ -2,87 +2,492 @@
<resources>
<!--GENERAL: Please put all strings inside quotes as described in example 1 on
http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
+ <string name="app_name">OpenKeychain</string>
<!--title-->
+ <string name="title_encrypt_text">Criptografar</string>
+ <string name="title_encrypt_files">Criptografar</string>
+ <string name="title_decrypt">Descriptografar</string>
+ <string name="title_add_subkey">Adicionar sub-chave</string>
+ <string name="title_edit_key">Editar chave</string>
+ <string name="title_linked_create">Criar uma identidade vinculada</string>
+ <string name="title_preferences">Configurações</string>
+ <string name="title_api_registered_apps">Apps</string>
+ <string name="title_key_server_preference">Servidores de chaves OpenPGP</string>
+ <string name="title_cache_ttl_preference">Personalize \'Lembretes\' de suas escolhas</string>
+ <string name="title_change_passphrase">Alterar senha</string>
+ <string name="title_share_fingerprint_with">Compartilhar impressão digital com...</string>
+ <string name="title_share_key">Compartilhar minha chave com...</string>
+ <string name="title_share_file">Compartilhar arquivo com...</string>
+ <string name="title_share_message">Compartilhar texto com...</string>
+ <string name="title_encrypt_to_file">Criptografar arquivo</string>
+ <string name="title_decrypt_to_file">Descriptografar arquivo</string>
+ <string name="title_import_keys">Importar chaves</string>
+ <string name="title_export_key">Backup de chave</string>
+ <string name="title_export_keys">Backup de chaves</string>
+ <string name="title_key_not_found">Chave não encontrada</string>
+ <string name="title_send_key">Enviar para o servidor de chaves</string>
+ <string name="title_backup">Backup chave</string>
+ <string name="title_certify_key">Confirmar chave</string>
+ <string name="title_key_details">Detalhes da chave</string>
+ <string name="title_help">Ajuda</string>
+ <string name="title_log_display">Registro</string>
+ <string name="title_exchange_keys">Troca de chaves</string>
+ <string name="title_advanced_key_info">Avançado</string>
+ <string name="title_delete_secret_key">Delete SUA chave \'%s\'?</string>
+ <string name="title_manage_my_keys">Gerenciar minhas chaves</string>
<!--section-->
+ <string name="section_user_ids">Identidades</string>
+ <string name="section_security_token">Token de segurança</string>
+ <string name="section_linked_system_contact">Sistema de contato vinculado</string>
+ <string name="section_keybase_proofs">Provas Keybase.io</string>
+ <string name="section_should_you_trust">Você confia nessa chave?</string>
+ <string name="section_proof_details">Prova de verificação</string>
+ <string name="section_keys">Sub-chaves</string>
+ <string name="section_cloud_search">Buscar chave</string>
+ <string name="section_cloud_search_summary">Keyserver, keybase.io</string>
+ <string name="section_passphrase_cache">Senhas e PINs</string>
+ <string name="section_passphrase_cache_summary">Manipulação, interface de usuário, lembrete de tempo</string>
+ <string name="section_proxy_settings">Anonimato de rede</string>
+ <string name="section_proxy_settings_summary">Tor, Configuração de proxy</string>
+ <string name="section_gui">Interface</string>
+ <string name="section_sync_settings">Sincronização</string>
+ <string name="section_sync_settings_summary">Atualização automática de chaves, vinculação de contato</string>
+ <string name="section_experimental_features">Características experimentais</string>
+ <string name="section_certify">Confirmar</string>
+ <string name="section_actions">Ações</string>
+ <string name="section_share_key">Chave</string>
+ <string name="section_key_server">Servidor de chaves</string>
+ <string name="section_fingerprint">Impressão digital</string>
+ <string name="section_phrases">Senhas</string>
+ <string name="section_encrypt">Criptografar</string>
+ <string name="section_decrypt">Descriptografar / Verificar</string>
+ <string name="section_current_expiry">Validade atual</string>
+ <string name="section_new_expiry">Nova data de expiração</string>
<!--button-->
+ <string name="btn_decrypt_verify_file">Descriptografar, verificar, e salvar arquivo.</string>
+ <string name="btn_encrypt_share_file">Criptografar e compartilhar arquivo</string>
+ <string name="btn_encrypt_save_file">Criptografar e salvar arquivo</string>
+ <string name="btn_save_file">Salvar arquivo</string>
+ <string name="btn_save">Salvar</string>
+ <string name="btn_view_log">Ver registro</string>
+ <string name="btn_do_not_save">Cancelar</string>
+ <string name="btn_delete">Deletar</string>
+ <string name="btn_no_date">Nunca expirar</string>
+ <string name="btn_okay">Ok</string>
+ <string name="btn_export_to_server">Enviar para o servidor de chaves</string>
+ <string name="btn_next">Proximo</string>
+ <string name="btn_back">Voltar</string>
+ <string name="btn_no">Não</string>
+ <string name="btn_match">Impressões digitais não conferem</string>
+ <string name="btn_match_phrases">Senhas não conferem</string>
+ <string name="btn_share_encrypted_signed">Criptogragar/Assinar e compartilhar texto</string>
+ <string name="btn_copy_encrypted_signed">Criptogragar/Assinar e copiar texto</string>
+ <string name="btn_paste_encrypted_signed">Criptogragar/Assinar e colar texto</string>
+ <string name="btn_view_cert_key">Ver certificação de chave</string>
+ <string name="btn_create_key">Criar chave</string>
+ <string name="btn_add_files">Adicionar arquivo(s)</string>
+ <string name="btn_share_decrypted_text">Compartilhar</string>
+ <string name="btn_open_with">Abrir com...</string>
+ <string name="btn_copy_decrypted_text">Copiar para área de transferência</string>
+ <string name="btn_decrypt_clipboard">Ler a partir da área de transferência</string>
+ <string name="btn_decrypt_files">Selecione um arquivo de entrada</string>
+ <string name="btn_encrypt_files">Criptografar arquivos</string>
+ <string name="btn_encrypt_text">Criptografar texto</string>
+ <string name="btn_add_email">Digite um endereço de e-mail adicional</string>
+ <string name="btn_unlock">Desbloquear</string>
+ <string name="btn_add_keyserver">Adicionar</string>
+ <string name="btn_save_default">Salvar como padrão</string>
+ <string name="btn_saved">Salvo!</string>
+ <string name="btn_not_matching">Não confere</string>
<!--Content Description-->
+ <string name="cd_encrypt_files">Criptografar arquivos</string>
+ <string name="cd_exchange_keys">Troca de chaves</string>
+ <string name="cd_encrypt_text">Criptografar texto</string>
+ <string name="cd_share_nfc">Compartilhar via NFC</string>
<!--menu-->
+ <string name="menu_preferences">Configurações</string>
+ <string name="menu_help">Ajuda</string>
+ <string name="menu_export_key">Backup da chave</string>
+ <string name="menu_delete_key">Detelar chave</string>
+ <string name="menu_manage_keys">Gerenciar minhas chaves</string>
+ <string name="menu_search">Buscar</string>
+ <string name="menu_nfc_preferences">Configurações NFC</string>
+ <string name="menu_beam_preferences">Configurações de feixe</string>
+ <string name="menu_encrypt_to">Criptografar para...</string>
+ <string name="menu_select_all">Selecionar tudo</string>
+ <string name="menu_export_all_keys">Exportar todas as chaves</string>
+ <string name="menu_update_all_keys">Atualizar todas as chaves</string>
+ <string name="menu_advanced">Avançado</string>
+ <string name="menu_certify_fingerprint">Confirmar com impressão digital</string>
+ <string name="menu_certify_fingerprint_phrases">Confirme com senhas</string>
+ <string name="menu_share_log">Compartilhar registro</string>
+ <string name="menu_change_password">Alterar senha</string>
+ <string name="menu_keyserver_add">Adicionar</string>
<!--label-->
+ <string name="label_message">Texto</string>
+ <string name="label_file">Arquivo</string>
+ <string name="label_files">Arquivo(s)</string>
+ <string name="label_file_colon">Arquivo:</string>
+ <string name="label_no_passphrase">Sem senha</string>
+ <string name="label_passphrase">Senha</string>
+ <string name="label_pin">PIN</string>
+ <string name="label_unlock">Desbloqueando...</string>
+ <string name="label_passphrase_again">Repita a senha</string>
+ <string name="label_show_passphrase">Mostre a senha</string>
+ <string name="label_algorithm">Algoritmo</string>
+ <string name="label_ascii_armor">Arquivo ASCII blindado</string>
+ <string name="label_file_ascii_armor">Habilitar ASCII blindado</string>
+ <string name="label_write_version_header">Deixe que outros saibam que você está usando o OpenKeychain</string>
+ <string name="label_write_version_header_summary">Escrever: \"v2.7 OpenKeychain \'para assinaturas OpenPGP, texto cifrado e chaves exportadas</string>
+ <string name="label_use_num_keypad_for_security_token_pin">Use o teclado numérico para Token de Segurança PIN</string>
+ <string name="label_asymmetric_from">Assinar com:</string>
+ <string name="label_to">Criptografar para:</string>
+ <string name="label_delete_after_encryption">Excluir arquivos após criptografia</string>
+ <string name="label_delete_after_decryption">Excluir após descriptografia</string>
+ <string name="label_encryption_algorithm">Algoritmo de criptografia</string>
+ <string name="label_hash_algorithm">Algoritmo de hash</string>
+ <string name="label_symmetric">Criptografar com senha</string>
+ <string name="label_passphrase_cache_ttl">Personalize \'Lembrar\' escolhas</string>
+ <string name="label_passphrase_cache_subs">Lembre-se de senhas por subchave</string>
+ <string name="label_message_compression">Compressão de texto</string>
+ <string name="label_file_compression">Compressão de arquivo</string>
+ <string name="label_keyservers">Selecionar servidores de chaves OpenPGP</string>
+ <string name="label_key_id">ID de chave</string>
+ <string name="label_key_created">Chave criada %s</string>
+ <string name="label_creation">Criação</string>
+ <string name="label_expiry">Data de expiração</string>
+ <string name="label_usage">Uso</string>
+ <string name="label_key_size">Tamanho da chave</string>
+ <string name="label_ecc_curve">Curva elíptica</string>
+ <string name="label_main_user_id">Identidade primária</string>
+ <string name="label_name">Nome</string>
+ <string name="label_comment">Comentário</string>
+ <string name="label_email">E-mail</string>
+ <string name="label_send_key">Sincronizar com a Internet</string>
+ <string name="label_fingerprint">Impressão digital</string>
+ <string name="expiry_date_dialog_title">Definir data de validade</string>
+ <string name="label_keyservers_title">Servidor de chaves</string>
+ <string name="label_keyserver_settings_hint">Arraste para alterar a ordem, toque para editar/apagar</string>
+ <string name="label_selected_keyserver_title">Selecione servidor de chaves</string>
+ <string name="label_preferred">Preferencial</string>
+ <string name="label_enable_compression">Habilitar a compactação</string>
+ <string name="label_encrypt_filenames">Criptografar os nomes de arquivo</string>
+ <string name="label_hidden_recipients">Esconder destinatários</string>
+ <string name="label_verify_keyserver_connection">Testar conexão</string>
+ <string name="label_only_trusted_keyserver">Apenas servidores de chaves confiáveis</string>
+ <string name="label_enter_keyserver_url">URL</string>
+ <string name="label_keyserver_dialog_delete">Excluir servidor de chave</string>
+ <string name="label_theme">Tema</string>
+ <string name="pref_keyserver">Servidores de chaves OpenPGP</string>
+ <string name="pref_keyserver_summary">Pesquisar chaves em servidores de chaves OpenPGP selecionados (protocolo HKP)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Buscar chaves em keybase.io</string>
+ <string name="pref_facebook">Facebook</string>
+ <string name="pref_facebook_summary">Buscar chaves no Facebook por nomes de usuário</string>
+ <string name="label_sync_settings_keyserver_title">Atualização automática de chaves</string>
+ <string name="label_sync_settings_keyserver_summary_on">A cada três dias, as chaves são atualizados a partir dos servidores de chaves selecionados</string>
+ <string name="label_sync_settings_keyserver_summary_off">Chaves não serão atualizadas automaticamente</string>
+ <string name="label_sync_settings_contacts_title">Vincular chaves aos contatos</string>
+ <string name="label_sync_settings_contacts_summary_on">Fazer a vinculação de chaves para contatos com base em nomes e endereços de e-mail. Isto acontece completamente off-line no seu dispositivo.</string>
+ <string name="label_sync_settings_contacts_summary_off">Novas chaves não será vinculadas aos contatos</string>
<!--label shown in Android settings under the OpenKeychain account-->
+ <string name="keyserver_sync_settings_title">Atualização automática de chaves</string>
+ <string name="label_experimental_settings_desc_title">Cuidado!</string>
+ <string name="label_experimental_settings_desc_summary">Esses recursos ainda não estão terminados ou resultam da experiência/segurança do usuário em pesquisa. Assim, não confia em sua segurança e por favor não relatar os problemas que encontrar nesta situação!</string>
+ <string name="label_experimental_settings_word_confirm_title">Confirmação com senhas</string>
+ <string name="label_experimental_settings_word_confirm_summary">Confirmar as chaves com senhas em vez de impressões digitais hexadecimais</string>
+ <string name="label_experimental_settings_linked_identities_title">Identidades vinculadas</string>
+ <string name="label_experimental_settings_linked_identities_summary">Vincule suas chaves para o Twitter, GitHub, websites ou DNS (semelhante ao keybase.io mas de forma descentralizada)</string>
+ <string name="label_experimental_settings_keybase_title">Provas Keybase.io</string>
+ <string name="label_experimental_settings_keybase_summary">Entre em contato com keybase.io para as provas de chaves e mostrar cada vez que uma chave for exibida.</string>
+ <string name="label_experimental_settings_theme_summary">(Os ícones e muitas telas ainda não estão ajustadas em conformidade com o tema escuro)</string>
<!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Ativar Tor</string>
+ <string name="pref_proxy_tor_summary">Requer que Orbot seja instalado</string>
+ <string name="pref_proxy_normal_title">Ativar outro proxy</string>
+ <string name="pref_proxy_host_title">Servidor proxy</string>
+ <string name="pref_proxy_host_err_invalid">Servidor de proxy não pode estar vazio</string>
+ <string name="pref_proxy_port_title">Porta de proxy</string>
+ <string name="pref_proxy_port_err_invalid">Entrada inválida de numero de porta</string>
+ <string name="pref_proxy_type_title">Tipo de proxy</string>
<!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
<!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Não use Tor</string>
<!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Instalar Orbot para usar Tor?</string>
+ <string name="orbot_install_dialog_install">Instalar</string>
+ <string name="orbot_install_dialog_content">Você deve ter o Orbot instalado e ativado para trafégo passar por ele. Você gostaria de instala-lo?</string>
+ <string name="orbot_install_dialog_cancel">Cancelar</string>
+ <string name="orbot_install_dialog_ignore_tor">Não use Tor</string>
<!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Iniciar orbot?</string>
+ <string name="orbot_start_dialog_content">Orbot não parece estar em execução. Você gostaria de iniciá-lo e conectar-se a rede Tor?</string>
+ <string name="orbot_start_btn">Iniciar Orbot</string>
+ <string name="orbot_start_dialog_start">Iniciar Orbot</string>
+ <string name="orbot_start_dialog_cancel">Cancelar</string>
+ <string name="orbot_start_dialog_ignore_tor">Não use Tor</string>
+ <string name="secret_key">Chave secreta:</string>
<!--choice-->
- <!--key flags-->
+ <string name="choice_none">Nenhum</string>
+ <string name="choice_15secs">15 secs</string>
+ <string name="choice_1min">1 min</string>
+ <string name="choice_3mins">3 mins</string>
+ <string name="choice_5mins">5 mins</string>
+ <string name="choice_10mins">10 mins</string>
+ <string name="choice_20mins">20 mins</string>
+ <string name="choice_40mins">40 mins</string>
+ <string name="choice_1hour">1 hora</string>
+ <string name="choice_2hours">2 horas</string>
+ <string name="choice_4hours">4 horas</string>
+ <string name="choice_8hours">8 horas</string>
+ <string name="choice_forever">Para sempre</string>
+ <string name="choice_select_cert">Selecione a chave</string>
+ <string name="filemanager_title_open">Abrir...</string>
+ <string name="error">Erro</string>
+ <string name="error_message">Erro: %s</string>
+ <string name="theme_dark">Escuro</string>
+ <string name="theme_light">Claro</string>
<!--sentences-->
+ <string name="wrong_passphrase">Senha incorreta.</string>
+ <string name="no_filemanager_installed">Não compativel com o gerenciador de arquivos instalado.</string>
+ <string name="passphrases_do_not_match">A senhas não conferem.</string>
+ <string name="passphrase_must_not_be_empty">Por favor digite a senha.</string>
+ <string name="passphrase_for_symmetric_encryption">Digite a senha</string>
+ <string name="passphrase_for_backup">Digite o codigo de backup</string>
+ <string name="passphrase_for">Digite a senha para \'%s\'</string>
+ <string name="pin_for">Digite o PIN para \'%s\'</string>
+ <string name="security_token_pin_for">Digite o PIN para acessar o Token de segurança para \'%s\'</string>
+ <string name="file_delete_confirmation_title">Deletar arquivos originais?</string>
+ <string name="public_key_deletetion_confirmation">Deletar chave \'%s\'?</string>
<!--errors
no punctuation, all lowercase,
they will be put after "error_message", e.g. "Error: file not found"-->
<!--errors without preceeding Error:-->
<!--results shown after decryption/verification-->
+ <string name="decrypt_result_signature_revoked_key">Assinado por uma chave <b>revogada!</b></string>
+ <string name="decrypt_invalid_text">Ou a assinatura é inválida ou a chave foi revogada. Você não pode ter certeza quem escreveu o texto. Você ainda quer exibi-lo?</string>
<!--Add keys-->
+ <string name="add_keys_my_key">Minha chave:</string>
<!--progress dialogs, usually ending in '…'-->
+ <string name="progress_done">Feito.</string>
+ <string name="progress_cancel">Cancelar</string>
+ <string name="progress_cancelling">Canelando...</string>
+ <string name="progress_saving">Salvando...</string>
+ <string name="progress_importing">Importando...</string>
+ <string name="progress_revoking_uploading">Revogando e atualizando chave...</string>
+ <string name="progress_updating">Atualizando chaves...</string>
+ <string name="progress_exporting">exportando...</string>
+ <string name="progress_uploading">enviando...</string>
+ <string name="progress_verifying_integrity">Verificando integridade</string>
+ <string name="progress_deleting">Excluindo chaves...</string>
+ <string name="progress_verifying_keyserver_connection">Verificando conexão</string>
+ <string name="progress_starting_orbot">Iniciando Orbot...</string>
<!--action strings-->
+ <string name="hint_cloud_search_hint">Buscando via Nome, e-mail...</string>
<!--key bit length selections-->
+ <string name="key_size_2048">2048</string>
+ <string name="key_size_3072">3072</string>
+ <string name="key_size_4096">4096</string>
+ <string name="key_size_8192">8192</string>
<!--elliptic curve names-->
+ <string name="key_curve_nist_p256">NIST P-256</string>
+ <string name="key_curve_nist_p384">NIST P-384</string>
+ <string name="key_curve_nist_p521">NIST P-521</string>
<!--not in for now, see SaveKeyringParcel
<string name="key_curve_bp_p256">"Brainpool P-256"</string>
<string name="key_curve_bp_p384">"Brainpool P-384"</string>
<string name="key_curve_bp_p512">"Brainpool P-512"</string>-->
<!--compression-->
+ <string name="compression_fast">rápido</string>
+ <string name="compression_very_slow">muito lento</string>
<!--Help-->
+ <string name="help_tab_start">Iniciar</string>
+ <string name="help_tab_faq">FAQ</string>
+ <string name="help_tab_wot">Confirmação de chave</string>
+ <string name="help_tab_nfc_beam">NFC Beam</string>
+ <string name="help_tab_about">Sobre</string>
+ <string name="help_about_version">Versão:</string>
+ <string name="help_tab_donations">Doar</string>
<!--Import-->
+ <string name="import_tab_keyserver">Servidor de chaves</string>
+ <string name="import_tab_cloud">Buscar chave</string>
+ <string name="import_import">Importar chaves selecionadas</string>
<!--Import from URL-->
<!--Generic result toast-->
<!--Import result toast-->
<!--Delete result toast-->
<!--Revoke result toast (snackbar)-->
+ <string name="revoke_ok">Chave revogada com sucesso!</string>
<!--Certify result toast-->
<!--Intent labels-->
<!--Remote API-->
+ <string name="api_settings_no_key">Nenhuma chave selecionada</string>
+ <string name="api_settings_select_key">Selecionar chave</string>
+ <string name="api_settings_create_key">Criar nova chave</string>
+ <string name="api_settings_save">Salvar</string>
+ <string name="api_settings_save_msg">A conta foi salva</string>
+ <string name="api_settings_cancel">Cancelar</string>
+ <string name="api_settings_revoke">Revogar acesso</string>
+ <string name="api_settings_start">Iniciar aplicação</string>
+ <string name="api_settings_delete_account">Deletar conta</string>
+ <string name="api_settings_package_name">Nome do pacote</string>
+ <string name="api_settings_package_certificate">SHA-256 de pacote certificado</string>
+ <string name="api_settings_advanced">Avançado</string>
+ <string name="api_settings_settings">Configurações</string>
<!--Share-->
+ <string name="share_qr_code_dialog_title">Compartilhar com código QR </string>
+ <string name="share_nfc_dialog">Compartilhar com NFC</string>
<!--retry upload dialog-->
<!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_message">Se você não quiser mais usar esta chave, ela deve ser revogada e carregada para o servidor. Selecione \'Deletar somente\' se você deseja apenas remover a chave do OpenKeychain, mas você continuará utilizando-a em outros locais.</string>
<!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
+ <string name="key_list_fab_search">Buscar chave</string>
<!--Key view-->
+ <string name="key_view_action_encrypt">Criptografar texto</string>
+ <string name="key_view_action_share_with">Compartilhar com...</string>
+ <string name="key_view_action_share_nfc">Compartilhar via NFC</string>
+ <string name="key_view_tab_start">Iniciar</string>
+ <string name="key_view_tab_share">Compartilhar</string>
+ <string name="key_view_tab_keys">Sub-chaves</string>
+ <string name="user_id_info_revoked_title">Revogada</string>
+ <string name="user_id_info_revoked_text">Esta identidade foi revogada pelo proprietário da chave. Ela não é mais válida.</string>
<!--Key trust-->
<!--keybase proof stuff-->
<!--Edit key-->
+ <string name="edit_key_action_change_passphrase">Alterar senha</string>
+ <string name="edit_key_edit_user_id_revoked">Esta identidade foi revogado. Está ação não pode ser desfeita.</string>
<!--Create key-->
+ <string name="create_key_upload">Sincronizar com a Internet</string>
+ <string name="create_key_yubi_key_pin">PIN</string>
<!--View key-->
+ <string name="view_key_revoked">Revogada: A chave não deve ser mais utilizada!</string>
+ <string name="view_key_my_key">Minha chave</string>
+ <string name="view_key_verified">Chave confirmada</string>
<!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Adicionar ao servidor de chaves</string>
+ <string name="edit_keyserver_dialog_title">Editar servidor de chaves</string>
+ <string name="add_keyserver_connection_verified">Conexão verificada!</string>
<!--Navigation Drawer-->
+ <string name="nav_keys">Chaves</string>
+ <string name="nav_encrypt_decrypt">Criptografar/Descriptografar</string>
+ <string name="nav_apps">Apps</string>
+ <string name="my_keys">Minhas chaves</string>
+ <string name="nav_backup">Backup/Restaurar</string>
<!--hints-->
<!--certs-->
+ <string name="cert_default">Padrão</string>
+ <string name="cert_none">nenhum</string>
+ <string name="cert_casual">casual</string>
+ <string name="cert_positive">positivo</string>
+ <string name="cert_revoke">revogada</string>
+ <string name="cert_verify_ok">OK</string>
+ <string name="cert_verify_failed">falhou!</string>
+ <string name="cert_verify_error">Erro!</string>
+ <string name="cert_verify_unavailable">chave indisponivel</string>
<!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
+ <string name="msg_internal_error">Erro interno!</string>
+ <string name="msg_cancelled">Operação cancelada.</string>
<!--Import Public log entries-->
+ <string name="msg_ip_master_flags_unspecified">Master flags: não especificado (assumindo todos)</string>
+ <string name="msg_ip_master_flags_cesa">Master flags: certificar, criptografar, assinar, autenticar</string>
+ <string name="msg_ip_master_flags_cesx">Master flags: certificar, criptografar, assinar</string>
+ <string name="msg_ip_master_flags_cexa">Master flags: certificar, criptografar, autenticar</string>
+ <string name="msg_ip_master_flags_cexx">Master flags: certificar, criptografar</string>
+ <string name="msg_ip_master_flags_cxsa">Master flags: certificar, assinar, autenticar</string>
+ <string name="msg_ip_master_flags_cxsx">Master flags: certificar, assinar</string>
+ <string name="msg_ip_master_flags_cxxa">Master flags: certificar, autenticar</string>
+ <string name="msg_ip_master_flags_cxxx">Master flags: certificar</string>
+ <string name="msg_ip_master_flags_xesa">Master flags: criptografar, assinar, autenticar</string>
+ <string name="msg_ip_master_flags_xesx">Master flags: criptografar, assinar</string>
+ <string name="msg_ip_master_flags_xexa">Master flags: criptografar, autenticar</string>
+ <string name="msg_ip_master_flags_xexx">Master flags: criptografar</string>
+ <string name="msg_ip_master_flags_xxsa">Master flags: assinar, autenticar</string>
+ <string name="msg_ip_master_flags_xxsx">Master flags: assinar</string>
+ <string name="msg_ip_master_flags_xxxa">Master flags: autenticar</string>
+ <string name="msg_ip_master_flags_xxxx">Master flags: nenhum</string>
+ <string name="msg_ip_subkey_flags_cesa">Sub-chaves flags: certificar, criptografar, assinar, autenticar</string>
+ <string name="msg_ip_subkey_flags_cesx">Sub-chaves flags: certificar, criptografar, assinar</string>
+ <string name="msg_ip_subkey_flags_cexa">Sub-chaves flags: certificar, criptografar, autenticar</string>
+ <string name="msg_ip_subkey_flags_cexx">Sub-chaves flags: certificar, criptografar</string>
+ <string name="msg_ip_subkey_flags_cxsa">Sub-chaves flags: certificar, assinar, autenticar</string>
+ <string name="msg_ip_subkey_flags_cxsx">Sub-chaves flags: certificar, assinar</string>
+ <string name="msg_ip_subkey_flags_cxxa">Sub-chaves flags: certificar, autenticar</string>
+ <string name="msg_ip_subkey_flags_cxxx">Sub-chaves flags: certificar</string>
+ <string name="msg_ip_subkey_flags_xesa">Sub-chaves flags: criptografar, assinar, autenticar</string>
+ <string name="msg_ip_subkey_flags_xesx">Sub-chaves flags: criptografar, assinar</string>
+ <string name="msg_ip_subkey_flags_xexa">Sub-chaves flags: criptografar, autenticar</string>
+ <string name="msg_ip_subkey_flags_xexx">Sub-chaves flags: criptografar</string>
+ <string name="msg_ip_subkey_flags_xxsa">Sub-chaves flags: assinar, autenticar</string>
+ <string name="msg_ip_subkey_flags_xxsx">Sub-chaves flags: assinar</string>
+ <string name="msg_ip_subkey_flags_xxxa">Sub-chaves flags: autenticar</string>
+ <string name="msg_ip_subkey_flags_xxxx">Sub-chaves flags: nenhum</string>
+ <string name="msg_ip_uid_revoked">Este ID está revogado</string>
+ <string name="msg_ip_uat_revoked">Atribuição do usuário é revogada</string>
<!--Import Secret log entries-->
<!--Keyring Canonicalization log entries-->
+ <string name="msg_kc_master_bad_type">Removendo certificado de chave mestra do tipo desconhecida (%s)</string>
+ <string name="msg_kc_uid_remove">Removendo ID de usuário inválido \'%s\'</string>
<!--Keyring merging log entries-->
<!--createSecretKeyRing-->
+ <string name="msg_cr_error_internal_pgp">Erro interno de OpenPGP!</string>
<!--modifySecretKeyRing-->
+ <string name="msg_mf_error_master_none">Nenhum certificado mestre foi encontrado para operar! (Todos revogados?)</string>
+ <string name="msg_mf_error_revoked_primary">IDs de usuário revogados não podem ser primários!</string>
+ <string name="msg_mf_error_pgp">Erro interno de OpenPGP!</string>
<!--Consolidate-->
<!--Edit Key (higher level than modify)-->
<!--Promote key-->
<!--Other messages used in OperationLogs-->
<!--Messages for DecryptVerify operation-->
<!--Messages for VerifySignedLiteralData operation-->
+ <string name="msg_vl_ok">OK</string>
<!--Messages for SignEncrypt operation-->
<!--Messages for PgpSignEncrypt operation-->
+ <string name="msg_pse_error_pgp">Erro interno de OpenPGP!</string>
+ <string name="msg_revoke_ok">Chave revogada com sucesso!</string>
<!--Linked Identity verification-->
+ <string name="msg_lv">Verificando identidades vinculadas...</string>
<!--Messages for Keybase Verification operation-->
<!--Messages for Mime parsing operation-->
<!--PassphraseCache-->
+ <string name="passp_cache_notif_pwd">Senha</string>
<!--Keyserver sync-->
+ <string name="keyserver_sync_orbot_notif_start">Iniciar Orbot</string>
<!--First Time-->
<!--unsorted-->
+ <string name="error_log_share_internal">Erro interno enquando ocorria processo de registro!</string>
<!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <string name="enter_passphrase">Digite a senha</string>
+ <string name="passphrase">Senha</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
+ <string name="nfc_settings">Configurações</string>
+ <string name="security_token_error">Erro: %s</string>
+ <string name="intent_share">Compartilhar conteúdo Assinado/Criptogradado</string>
<!--Other Linked Identity strings-->
+ <string name="section_linked_identities">Identidades vinculadas</string>
+ <string name="linked_title_github">GitHub</string>
+ <string name="linked_title_twitter">Twitter</string>
+ <string name="card_linked_identity">Identidade vinculada</string>
+ <string name="linked_button_confirm">Confirmar</string>
+ <string name="linked_text_error">Erro</string>
+ <string name="title_linked_id_create">Criar identidade vinculada</string>
+ <string name="linked_gist_description">Identidade vinculada do OpenKeychain</string>
+ <string name="btn_backup_share">Compartilhar backup</string>
+ <string name="snack_shared_text_too_long">O texto compartilhado foi cortado porque era muito longo!</string>
+ <string name="share_log_dialog_title">Compartilhar registros?</string>
+ <string name="share_log_dialog_message">Enquanto registros podem ser super úteis para os desenvolvedores encontrarem falhas no OpenKeychain, eles podem conter informações confidenciais em potencial, tais como, dados sobre atualização de chaves. Por favor, verifique se é necessário compartilhar tais informações.</string>
+ <string name="share_log_dialog_share_button">Compartilhar</string>
+ <string name="share_log_dialog_cancel_button">Cancelar</string>
+ <string name="menu_uids_save">Salvar</string>
+ <string name="cache_ttl_forever">Para sempre</string>
+ <string name="prompt_fidesmo_pgp_install_button_positive">Instalar</string>
+ <string name="prompt_fidesmo_pgp_install_button_negative">Cancelar</string>
+ <string name="prompt_fidesmo_app_install_button_positive">Instalar</string>
+ <string name="prompt_fidesmo_app_install_button_negative">Cancelar</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml
index d98d9d1d8..7f276ef51 100644
--- a/OpenKeychain/src/main/res/values-ru/strings.xml
+++ b/OpenKeychain/src/main/res/values-ru/strings.xml
@@ -8,6 +8,7 @@
<string name="title_encrypt_files">Зашифровать файлы</string>
<string name="title_decrypt">Расшифровать</string>
<string name="title_add_subkey">Добавить подключ</string>
+ <string name="title_change_master_key">Изменить основной ключ</string>
<string name="title_edit_key">Изменить ключ</string>
<string name="title_linked_create">Создать связанный идентификатор</string>
<string name="title_preferences">Настройки</string>
@@ -35,6 +36,7 @@
<string name="title_advanced_key_info">Дополнительно</string>
<string name="title_delete_secret_key">Удалить ВАШ ключ \'%s\'?</string>
<string name="title_manage_my_keys">Управление ключами</string>
+ <string name="title_alert_strip">Отделить этот доп. ключ</string>
<!--section-->
<string name="section_user_ids">Идентификаторы</string>
<string name="section_security_token">Токены безопасности</string>
@@ -46,6 +48,7 @@
<string name="section_cloud_search">Поиск ключа</string>
<string name="section_cloud_search_summary">Сервер ключей, keybase.io</string>
<string name="section_passphrase_cache">Пароли и PIN-коды</string>
+ <string name="section_passphrase_cache_summary">Обработка, пользовательский интерфейс, время запоминания</string>
<string name="section_proxy_settings">Сетевая анонимность</string>
<string name="section_proxy_settings_summary">Tor, настройки прокси</string>
<string name="section_gui">Интерфейс</string>
@@ -85,8 +88,9 @@
<string name="btn_view_cert_key">Просмотр ключа</string>
<string name="btn_create_key">Создать ключ</string>
<string name="btn_add_files">Добавить файл(ы)</string>
- <string name="btn_share_decrypted_text">Опубликовать</string>
+ <string name="btn_share_decrypted_text">Отправить</string>
<string name="btn_open_with">Открыть с помощью...</string>
+ <string name="btn_copy_decrypted_text">Копировать в буфер</string>
<string name="btn_decrypt_clipboard">Прочитать из буфера</string>
<string name="btn_decrypt_files">Выберите входной файл</string>
<string name="btn_encrypt_files">Зашифровать файлы</string>
@@ -137,6 +141,7 @@
<string name="label_file_ascii_armor">Использовать ASCII формат</string>
<string name="label_write_version_header">Добавить комментарий об использовании OpenKeychain</string>
<string name="label_write_version_header_summary">Дописывать \'OpenKeychain v2.x\' в OpenPGP подписи, шифры, и экспортируемые ключи</string>
+ <string name="label_use_num_keypad_for_security_token_pin">Используйте цифровую клавиатуру для PIN-кода токена безопасности</string>
<string name="label_asymmetric_from">Подписать:</string>
<string name="label_to">Зашифровать для</string>
<string name="label_delete_after_encryption">Удалить файлы после шифрования</string>
@@ -151,6 +156,7 @@
<string name="label_keyservers">Выберите серверы OpenPGP</string>
<string name="label_key_id">ID ключа</string>
<string name="label_key_created">Создан ключ %s</string>
+ <string name="label_key_type">Тип</string>
<string name="label_creation">Создан</string>
<string name="label_expiry">Годен до</string>
<string name="label_usage">Применение</string>
@@ -160,7 +166,7 @@
<string name="label_name">Имя</string>
<string name="label_comment">Комментарий</string>
<string name="label_email">Email</string>
- <string name="label_send_key">Синхронизация с Интернет</string>
+ <string name="label_send_key">Синхронизация с интернетом</string>
<string name="label_fingerprint">Отпечаток</string>
<string name="expiry_date_dialog_title">Срок годности</string>
<string name="label_keyservers_title">Серверы ключей</string>
@@ -172,16 +178,19 @@
<string name="label_hidden_recipients">Скрыть получателей</string>
<string name="label_verify_keyserver_connection">Тест соединения</string>
<string name="label_only_trusted_keyserver">Только доверенные сервера ключей</string>
+ <string name="label_enter_keyserver_url">URL</string>
<string name="label_keyserver_dialog_delete">Удалить сервер ключей</string>
<string name="label_theme">Тема</string>
<string name="pref_keyserver">Серверы OpenPGP</string>
<string name="pref_keyserver_summary">Искать ключи на выбранных серверах OpenPGP (протокол HKP)</string>
<string name="pref_keybase">keybase.io</string>
<string name="pref_keybase_summary">Искать ключи на Keybase.io</string>
+ <string name="pref_facebook">Facebook</string>
<string name="pref_facebook_summary">Искать ключи на Facebook по имени пользователя</string>
<string name="label_sync_settings_keyserver_title">Автообновление ключей</string>
<string name="label_sync_settings_keyserver_summary_on">Каждые три дня ключи будут обновляться с предпочитаемого сервера ключей</string>
<string name="label_sync_settings_keyserver_summary_off">Ключи не обновляются автоматически</string>
+ <string name="label_sync_settings_wifi_title">Синхронизация только по WiFi</string>
<string name="label_sync_settings_contacts_title">Связать ключи с контактами</string>
<string name="label_sync_settings_contacts_summary_on">Связывать ключи с контактами основываясь на именах и адресах электронной почты. Это происходит полностью в автономном режиме на вашем устройстве.</string>
<string name="label_sync_settings_contacts_summary_off">Новые ключи не будут связаны с контактами</string>
@@ -192,8 +201,8 @@
<string name="label_experimental_settings_word_confirm_title">Подтверждение фразами</string>
<string name="label_experimental_settings_word_confirm_summary">Подтвердить ключи с помощью фраз, вместо шестнадцатеричных отпечатков </string>
<string name="label_experimental_settings_linked_identities_title">Связанные идентификаторы</string>
- <string name="label_experimental_settings_linked_identities_summary">Привязать ключи к Twitter, GitHub, веб-сайту или DNS (по аналогии с keybase.io но децентрализованно)</string>
- <string name="label_experimental_settings_keybase_title">Keybase.io подтверждение</string>
+ <string name="label_experimental_settings_linked_identities_summary">Привязать ключи к Twitter, GitHub, веб-сайту или DNS (по аналогии с keybase.io, но децентрализованно)</string>
+ <string name="label_experimental_settings_keybase_title">Подтверждение Keybase.io </string>
<string name="label_experimental_settings_keybase_summary">Опрашивать keybase.io для подтверждения ключей и показывать это каждый раз при отображении ключей</string>
<string name="label_experimental_settings_theme_summary">(Значки и многие экраны ещё не скорректированы для тёмной темы)</string>
<!--Proxy Preferences-->
@@ -223,6 +232,8 @@
<string name="orbot_start_dialog_start">Запустить Orbot</string>
<string name="orbot_start_dialog_cancel">Отмена</string>
<string name="orbot_start_dialog_ignore_tor">Не использовать Tor</string>
+ <string name="user_id_no_name"><![CDATA[<без имени>]]></string>
+ <string name="none"><![CDATA[<нет>]]></string>
<plurals name="n_keys">
<item quantity="one">1 ключ</item>
<item quantity="few">%d ключей</item>
@@ -251,21 +262,26 @@
<string name="choice_8hours">8 часов</string>
<string name="choice_forever">всегда</string>
<string name="choice_select_cert">Выбрать ключ</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Открыть...</string>
+ <string name="rsa_2048">RSA 2048</string>
+ <string name="rsa_2048_description_html">меньший размер файла, считается безопасными до 2030 года</string>
+ <string name="rsa_3072">RSA 3072</string>
+ <string name="rsa_3072_description_html">рекомендуется, считается безопасным до 2040 года</string>
+ <string name="rsa_4096">RSA 4096</string>
+ <string name="rsa_4096_description_html">больший размер файла, считается безопасным и после 2040 года</string>
+ <string name="ecc_p256">ECC P-256</string>
+ <string name="ecc_p256_description_html">очень маленький размер файла, считается безопасным до 2040 года &lt;br/&gt; &lt;u&gt;экспериментально и поддерживается не всеми реализациями&lt;/u&gt;</string>
+ <string name="ecc_p521">ECC P-521</string>
+ <string name="ecc_p521_description_html">маленький размер файла, считается безопасным и после 2040 года &lt;br/&gt; &lt;u&gt;экспериментально и поддерживается не всеми реализациями&lt;/u&gt;</string>
+ <string name="usage_none">Нет (только привязка доп. ключа)</string>
+ <string name="usage_sign">Подписать</string>
+ <string name="usage_encrypt">Шифровать</string>
+ <string name="usage_sign_and_encrypt">Подписать и зашифровать</string>
<string name="error">Ошибка</string>
<string name="error_message">Ошибка: %s</string>
<string name="theme_dark">Тёмная</string>
<string name="theme_light">Светлая</string>
- <!--key flags-->
- <string name="flag_certify">Сертификация</string>
- <string name="flag_sign">Подписание</string>
- <string name="flag_encrypt">Шифрование</string>
- <string name="flag_authenticate">Аутентификация</string>
+ <string name="strip">Отделить</string>
<!--sentences-->
<string name="wrong_passphrase">Неправильный пароль</string>
<string name="no_filemanager_installed">Нет совместимого менеджера файлов.</string>
@@ -276,8 +292,14 @@
<string name="passphrase_for">Введите пароль для \'%s\'</string>
<string name="pin_for">Введите PIN для
\'%s\'</string>
+ <string name="security_token_pin_for">Введите PIN-код для доступа к токену безопасности \'%s\'</string>
+ <string name="security_token_nfc_text">Держите токен безопасности возле NFC-модуля с задней части вашего устройства.</string>
+ <string name="security_token_nfc_wait">Держите токен безопасности возле задней части!.</string>
+ <string name="security_token_nfc_finished">Теперь уберите токен безопасности.</string>
+ <string name="security_token_nfc_try_again_text">Уберите токен безопасности и нажмите ПОПРОБОВАТЬ ЕЩЁ РАЗ.</string>
<string name="file_delete_confirmation_title">Удалить исходные файлы?</string>
<string name="file_delete_confirmation">Следующие файлы будут удалены:%s</string>
+ <string name="file_delete_successful">%1$d из %2$d файлов было удалено.%3$s</string>
<string name="no_file_selected">Файл не выбран</string>
<string name="encrypt_sign_successful">Успешно подписано и/или зашифровано.</string>
<string name="encrypt_sign_clipboard_successful">Успешно подписано и/или зашифровано в буфер обмена.</string>
@@ -286,6 +308,7 @@
<string name="specify_file_to_encrypt_to">Пожалуйста, укажите, в какой файл произвести шифрование.\nВНИМАНИЕ: Файл будет перезаписан, если он уже существует!</string>
<string name="specify_file_to_decrypt_to">Пожалуйста, укажите, в какой файл произвести расшифровку.\nВНИМАНИЕ: Файл будет перезаписан, если он уже существует!</string>
<string name="specify_backup_dest">Будет произведено резервное копирование без ваших ключей , просьба указать файл назначения.\nВНИМАНИЕ! Файл будет перезаписан, если он существует!</string>
+ <string name="specify_backup_dest_single">Этот ключ будет опубликован, пожалуйста, укажите файл назначения.\nВНИМАНИЕ! Файл будет переписан, если он существует!</string>
<string name="specify_backup_dest_secret_single">Будет произведено полное резервное копирование ваших ключей, просьба указать файл назначения.\nВНИМАНИЕ! Файл будет переписан, если он существует!</string>
<string name="specify_backup_dest_secret">Будет произведено полное резервное копирование всех ключей, включая ваш, просьба указать файл назначения.\nВНИМАНИЕ! Файл будет переписан, если он существует!</string>
<string name="key_deletion_confirmation_multi">Вы правда хотите удалить выбранные ключи?</string>
@@ -293,11 +316,18 @@
<string name="public_key_deletetion_confirmation">Удалить ключ \'%s\'?</string>
<string name="also_export_secret_keys">Экспортировать секретные ключи</string>
<string name="reinstall_openkeychain">Вы столкнулись с багом Андроид. Пожалуйста, переустановите OpenKeychain чтобы связать ваши контакты и ключи. </string>
+ <string name="alert_strip">Отделение данного доп. ключа сделает его непригодным для использования на этом устройстве!</string>
<string name="key_exported">Успешный экспорт 1 ключа.</string>
<string name="keys_exported">Экспортировано %d ключей.</string>
<string name="no_keys_exported">Ключи не были экспортированы.</string>
<string name="key_creation_el_gamal_info">Прим.: только вторичные ключи поддерживают ElGamal.</string>
<string name="key_not_found">Не удается найти ключ %08X.</string>
+ <plurals name="bad_keys_encountered">
+ <item quantity="one">%d плохой секретный ключ проигнорирован. Возможно, вы экспортируете с параметром\n --export-secret-subkeys\nВместо этого используйте\n --export-secret-keys.\"</item>
+ <item quantity="few">%d плохих секретных ключа проигнорировано. Возможно, вы экспортируете с параметром\n --export-secret-subkeys\nВместо этого используйте\n --export-secret-keys.\"</item>
+ <item quantity="many">%d плохих секретных ключей проигнорировано. Возможно, вы экспортируете с параметром\n --export-secret-subkeys\nВместо этого используйте\n --export-secret-keys.\"</item>
+ <item quantity="other">%d плохих секретных ключей проигнорировано. Возможно, вы экспортируете с параметром\n --export-secret-subkeys\nВместо этого используйте\n --export-secret-keys.\"</item>
+ </plurals>
<string name="list_empty">Список пуст!</string>
<string name="nfc_successful">Ключ успешно передан через NFC!</string>
<string name="key_copied_to_clipboard">Ключ скопирован в буфер обмена!</string>
@@ -311,6 +341,7 @@
<string name="error_file_delete_failed">не удалены. Удалите их самостоятельно!</string>
<string name="error_file_added_already">%s уже был добавлен.</string>
<string name="error_file_not_found">файл не найден</string>
+ <string name="error_bad_data">Некорректные данные!</string>
<string name="error_no_secret_key_found">нет подходящего секретного ключа</string>
<string name="error_external_storage_not_ready">внешняя память не готова</string>
<string name="error_key_size_minimum512bit">размер ключа должен быть не менее 512бит</string>
@@ -381,6 +412,7 @@
<string name="progress_modify_subkeyadd">добавление доп. ключей...</string>
<string name="progress_modify_passphrase">изменение пароля...</string>
<string name="progress_modify_pin">смена PIN-кода…</string>
+ <string name="progress_modify_admin_pin">изменение PIN-кода администратора…</string>
<plurals name="progress_exporting_key">
<item quantity="one">экспорт ключа...</item>
<item quantity="few">экспорт ключей...</item>
@@ -458,9 +490,57 @@
<string name="with_warnings">, с предупреждениями</string>
<string name="with_cancelled">, до отмены</string>
<!--Import result toast-->
+ <plurals name="import_keys_added_and_updated_1">
+ <item quantity="one">Успешно импортирован один ключ</item>
+ <item quantity="few">Успешно импортировано %1$d ключа</item>
+ <item quantity="many">Успешно импортировано %1$d ключей</item>
+ <item quantity="other">Успешно импортировано %1$d ключей</item>
+ </plurals>
+ <plurals name="import_keys_added_and_updated_2">
+ <item quantity="one">и обновлён один ключ%2$s.</item>
+ <item quantity="few">и обновлено %1$d ключа%2$s.</item>
+ <item quantity="many">и обновлено %1$d ключей%2$s.</item>
+ <item quantity="other">и обновлено %1$d ключей%2$s.</item>
+ </plurals>
+ <plurals name="import_keys_added">
+ <item quantity="one">Успешно импортирован ключ%2$s.</item>
+ <item quantity="few">Успешно импортировано %1$d ключа%2$s.</item>
+ <item quantity="many">Успешно импортировано %1$d ключей%2$s.</item>
+ <item quantity="other">Успешно импортировано %1$d ключей%2$s.</item>
+ </plurals>
+ <plurals name="import_keys_updated">
+ <item quantity="one">Успешно обновлён ключ%2$s.</item>
+ <item quantity="few">Успешно обновлено %1$d ключа%2$s.</item>
+ <item quantity="many">Успешно обновлено %1$d ключей%2$s.</item>
+ <item quantity="other">Успешно обновлено %1$d ключей%2$s.</item>
+ </plurals>
+ <plurals name="import_error">
+ <item quantity="one">Импортирование ключа завершилось неудачно!</item>
+ <item quantity="few">Импортирование %d ключей завершилось неудачно!</item>
+ <item quantity="many">Импортирование %d ключей завершилось неудачно!</item>
+ <item quantity="other">Импортирование %d ключей завершилось неудачно!</item>
+ </plurals>
<string name="import_error_nothing">Нет данных для импорта.</string>
<string name="import_error_nothing_cancelled">Импорт отменен.</string>
<!--Delete result toast-->
+ <plurals name="delete_ok_but_fail_2">
+ <item quantity="one">, но не удалось удалить ключ%2$s.</item>
+ <item quantity="few">, но не удалось удалить %1$d ключа%2$s.</item>
+ <item quantity="many">, но не удалось удалить %1$d ключей%2$s.</item>
+ <item quantity="other">, но не удалось удалить %1$d ключей%2$s.</item>
+ </plurals>
+ <plurals name="delete_ok">
+ <item quantity="one">Успешно удалён ключ%2$s.</item>
+ <item quantity="few">Успешно удалено %1$d ключа%2$s.</item>
+ <item quantity="many">Успешно удалено %1$d ключей%2$s.</item>
+ <item quantity="other">Успешно удалено %1$d ключей%2$s.</item>
+ </plurals>
+ <plurals name="delete_fail">
+ <item quantity="one">Ошибка при удалении ключа%2$s.</item>
+ <item quantity="few">Ошибка при удалении %1$d ключей.</item>
+ <item quantity="many">Ошибка при удалении %1$d ключей.</item>
+ <item quantity="other">Ошибка при удалении %1$d ключей.</item>
+ </plurals>
<string name="delete_nothing">Нет данных для удаления!</string>
<string name="delete_cancelled">Удаление отменено.</string>
<!--Revoke result toast (snackbar)-->
@@ -469,6 +549,24 @@
<string name="revoke_nothing">Нечего аннулировать.</string>
<string name="revoke_cancelled">Операция аннулирования отменена.</string>
<!--Certify result toast-->
+ <plurals name="certify_keys_ok">
+ <item quantity="one">Успешно подтверждён ключ%2$s.</item>
+ <item quantity="few">Успешно подтверждено %1$d ключа%2$s.</item>
+ <item quantity="many">Успешно подтверждено %1$d ключей%2$s.</item>
+ <item quantity="other">Успешно подтверждено %1$d ключей%2$s.</item>
+ </plurals>
+ <plurals name="certify_keys_with_errors">
+ <item quantity="one">Сертификация не удалась!</item>
+ <item quantity="few">Сертификация для %d ключей не удалась!</item>
+ <item quantity="many">Сертификация для %d ключей не удалась!</item>
+ <item quantity="other">Сертификация для %d ключей не удалась!</item>
+ </plurals>
+ <plurals name="certify_error">
+ <item quantity="one">Сертификация не удалась!</item>
+ <item quantity="few">Сертификация %d ключей не удалась!</item>
+ <item quantity="many">Сертификация %d ключей не удалась!</item>
+ <item quantity="other">Сертификация %d ключей не удалась!</item>
+ </plurals>
<!--Intent labels-->
<string name="intent_decrypt_file">OpenKeychain: Расшифровать файл</string>
<string name="intent_import_key">OpenKeychain: Импортировать ключ</string>
@@ -487,6 +585,7 @@
<string name="api_settings_start">Запустить приложение</string>
<string name="api_settings_delete_account">Удалить аккаунт</string>
<string name="api_settings_package_name">Наименование пакета</string>
+ <string name="api_settings_package_certificate">SHA-256 сертификата пакета</string>
<string name="api_settings_accounts">Аккаунты (устаревший API)</string>
<string name="api_settings_advanced">Дополнительно</string>
<string name="api_settings_allowed_keys">Разрешённые ключи</string>
@@ -553,21 +652,24 @@
<string name="user_id_info_revoked_title">Аннулировано</string>
<string name="user_id_info_revoked_text">Этот идентификатор отозван владельцем ключа. Он больше недействителен.</string>
<string name="user_id_info_certified_title">Подтверждён</string>
- <string name="user_id_info_certified_text">Эта идентичность была подтверждена Вами.</string>
+ <string name="user_id_info_certified_text">Эта идентичность была подтверждена вами.</string>
<string name="user_id_info_uncertified_title">Не подтверждён</string>
<string name="user_id_info_uncertified_text">Эта идентичность ещё не была подтверждена. Вы не можете быть уверены, соответствует ли идентичность действительно определённому человеку.</string>
<string name="user_id_info_invalid_title">Недействительно</string>
<string name="user_id_info_invalid_text">Что-то не так с идентификатором!</string>
<!--Key trust-->
- <string name="key_trust_no_cloud_evidence">Отсутствуют подтверждения из Интернета о достоверности этого ключа.</string>
+ <string name="key_trust_no_cloud_evidence">Отсутствуют подтверждения из интернета о достоверности этого ключа.</string>
<string name="key_trust_start_cloud_search">Начать поиск</string>
<string name="key_trust_results_prefix">Keybase.io предлагает “доказательства”, которые подтверждают, что владелец этого ключа:</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Публикация на Twitter как %s</string>
+ <string name="keybase_narrative_twitter">Пишет на Twitter как %s</string>
<string name="keybase_narrative_github">Известен на GitHub как %s</string>
- <string name="keybase_narrative_reddit">Публикация на Reddit как %s</string>
+ <string name="keybase_narrative_dns">Контроль доменных имён %s</string>
+ <string name="keybase_narrative_web_site">Возможно разместить на веб-сайте(ах) %s</string>
+ <string name="keybase_narrative_reddit">Пишет на Reddit как %s</string>
<string name="keybase_narrative_coinbase">Известен на Coinbase как %s</string>
- <string name="keybase_narrative_hackernews">Публикация на Hacker News как %s</string>
+ <string name="keybase_narrative_hackernews">Пишет на Hacker News как %s</string>
+ <string name="keybase_narrative_unknown">Неизвестный тип доказательства %s</string>
<string name="keybase_proof_failure">К сожалению это доказательство не может быть верифицировано.</string>
<string name="keybase_unknown_proof_failure">Неопознанная проблема с проверкой доказательства </string>
<string name="keybase_problem_fetching_evidence">Проблема с доказательством</string>
@@ -577,6 +679,7 @@
<string name="keybase_message_payload_mismatch">Расшифрованная доказательная запись не соответствует ожидаемому значению</string>
<string name="keybase_message_fetching_data">Получение подтверждения</string>
<string name="keybase_proof_succeeded">Подтверждение успешно проверено!</string>
+ <string name="keybase_fetched_from">извлечено из</string>
<string name="keybase_for_the_domain">для домена</string>
<string name="keybase_contained_signature">содержит сообщение, которое могло быть создано только владельцем этого ключа.</string>
<string name="keybase_dns_proof">Запись DNS TXT</string>
@@ -597,14 +700,20 @@
</string-array>
<string name="edit_key_edit_user_id_revoked">Этот идентификатор был отозван. Это не может быть отменено.</string>
<string name="edit_key_edit_subkey_title">Выберите действие!</string>
+ <string-array name="edit_key_edit_subkey">
+ <item>Изменить срок действия</item>
+ <item>Аннулировать доп. ключ</item>
+ <item>Отделить доп. ключ</item>
+ <item>Переместить доп. ключ в токен безопасности</item>
+ </string-array>
<string name="edit_key_new_subkey">новый доп. ключ</string>
- <string name="edit_key_select_flag">Пожалуйста, выберите хотя бы один флаг!</string>
+ <string name="edit_key_select_usage">Пожалуйста, выберите используемый ключ!</string>
<string name="edit_key_error_add_identity">Добавьте хотя бы один идентификатор!</string>
<string name="edit_key_error_add_subkey">Добавьте хотя бы один доп. ключ!</string>
<string name="edit_key_error_bad_security_token_algo">Алгоритм не поддерживается токеном безопасности!</string>
<string name="edit_key_error_bad_security_token_size">Размер ключа не поддерживается токеном безопасности!</string>
<!--Create key-->
- <string name="create_key_upload">Синхронизация с Интернет</string>
+ <string name="create_key_upload">Синхронизация с интернетом</string>
<string name="create_key_empty">Это обязательне поле</string>
<string name="create_key_passphrases_not_equal">Пароли не совпадают</string>
<string name="create_key_final_text">Вы указали следующие данные:</string>
@@ -621,7 +730,9 @@
<string name="create_key_email_already_exists_text">Почтовый адрес уже был добавлен</string>
<string name="create_key_email_invalid_email">Неправильный формат почтового адреса</string>
<string name="create_key_yubi_key_pin_text">Пожалуйста, выберите PIN-код с 6 цифрами.</string>
+ <string name="create_key_yubi_key_admin_pin_text">Пожалуйста, запишите PIN-код администратора и храните его в надёжном месте (он потребуется, если будет 3 раза неправильно ввёден PIN-код).</string>
<string name="create_key_yubi_key_pin">PIN-код</string>
+ <string name="create_key_yubi_key_admin_pin">PIN-код администратора</string>
<string name="create_key_yubi_key_pin_repeat">Повторите PIN-код</string>
<string name="create_key_yubi_key_pin_not_correct">PIN-код неверен!</string>
<string name="create_key_yubi_key_pin_too_short">PIN-код должен быть не менее 6 цифр!</string>
@@ -640,6 +751,8 @@
<string name="add_keyserver_connection_verified">Соединение проверено!</string>
<string name="add_keyserver_without_verification">Сервер ключей добавлен без подтверждения.</string>
<string name="add_keyserver_invalid_url">Неправильный адрес!</string>
+ <string name="add_keyserver_keyserver_not_trusted">Сервер ключей не является одним из доверенных (за ним не закреплён сертификат)!</string>
+ <string name="add_keyserver_connection_failed">Не удалось подключиться к серверу ключей. Пожалуйста, проверьте URL и подключение к интернету.</string>
<string name="keyserver_preference_deleted">%s удалено</string>
<string name="keyserver_preference_cannot_delete_last">Невозможно удалить последний сервер ключей. По крайней мере один требуется!</string>
<!--Navigation Drawer-->
@@ -732,7 +845,19 @@
<string name="msg_ip_uid_cert_new">Сертификат более новый, заменяем предыдущий.</string>
<string name="msg_ip_uid_cert_good">Найден хороший сертификат %1$s</string>
<string name="msg_ip_uid_cert_good_revoke">Найдено аннулирование хорошего сертификата %1$s</string>
+ <plurals name="msg_ip_uid_certs_unknown">
+ <item quantity="one">Игнорирование сертификата, выданного неизвестным публичным ключом</item>
+ <item quantity="few">Игнорирование %s сертификатов, выданных неизвестными публичными ключами</item>
+ <item quantity="many">Игнорирование %s сертификатов, выданных неизвестными публичными ключами</item>
+ <item quantity="other">Игнорирование %s сертификатов, выданных неизвестными публичными ключами</item>
+ </plurals>
<string name="msg_ip_uid_classifying_zero">Сортировка пользовательских ID (доверенные ключи недоступны)</string>
+ <plurals name="msg_ip_uid_classifying">
+ <item quantity="one">Классификация ID пользователей (используя 1 доверенный ключ)</item>
+ <item quantity="few">Классификация ID пользователей (используя %s доверенных ключа)</item>
+ <item quantity="many">Классификация ID пользователей (используя %s доверенных ключей)</item>
+ <item quantity="other">Классификация ID пользователей (используя %s доверенных ключей)</item>
+ </plurals>
<string name="msg_ip_uid_reorder">Переупорядочение пользовательских ID</string>
<string name="msg_ip_uid_processing">Обработка id %s</string>
<string name="msg_ip_uid_revoked">id аннулирован</string>
@@ -745,6 +870,12 @@
<string name="msg_ip_uat_cert_new">Сертификат более новый, заменяем предыдущий.</string>
<string name="msg_ip_uat_cert_good">Найден хороший сертификат %1$s</string>
<string name="msg_ip_uat_cert_good_revoke">Найдено аннулирование хорошего сертификата %1$s</string>
+ <plurals name="msg_ip_uat_certs_unknown">
+ <item quantity="one">Игнорирование сертификата, выданного неизвестным публичным ключом</item>
+ <item quantity="few">Игнорирование %s сертификатов, выданных неизвестными публичными ключами</item>
+ <item quantity="many">Игнорирование %s сертификатов, выданных неизвестными публичными ключами</item>
+ <item quantity="other">Игнорирование %s сертификатов, выданных неизвестными публичными ключами</item>
+ </plurals>
<string name="msg_ip_uat_classifying">Сортировка атрибутов пользователя</string>
<string name="msg_ip_uat_revoked">Атрибут пользователя аннулирован</string>
<string name="msg_is_bad_type_public">Попытка импорта публичной связки как секретной. Это ошибка, пожалуйста, сообщите об этом.</string>
@@ -803,7 +934,19 @@
<string name="msg_kc_sub_algo_bad_encrpyt">Доп. ключ имеет флаг использования шифрования, но алгоритм не подходит для шифрования.</string>
<string name="msg_kc_sub_algo_bad_sign">Доп. ключ имеет флаг использования подписи, но алгоритм не подходит для подписи.</string>
<string name="msg_kc_success">Связка нормализована успешно, нет изменений</string>
+ <plurals name="msg_kc_success_bad">
+ <item quantity="one">Связка нормализована успешно, удалён один ошибочный сертификат</item>
+ <item quantity="few">Связка нормализована успешно, удалено %d ошибочных сертификата</item>
+ <item quantity="many">Связка нормализована успешно, удалено %d ошибочных сертификатов</item>
+ <item quantity="other">Связка нормализована успешно, удалено %d ошибочных сертификатов</item>
+ </plurals>
<string name="msg_kc_success_bad_and_red">Связка нормализована успешно, удалено %1$s ошибочных и %2$s лишних сертификатов</string>
+ <plurals name="msg_kc_success_redundant">
+ <item quantity="one">Связка нормализована успешно, удалён один лишний сертификат</item>
+ <item quantity="few">Связка нормализована успешно, удалено %d лишних сертификата</item>
+ <item quantity="many">Связка нормализована успешно, удалено %d лишних сертификатов</item>
+ <item quantity="other">Связка нормализована успешно, удалено %d лишних сертификатов</item>
+ </plurals>
<string name="msg_kc_uid_bad_err">Удаление плохого самостоятельного сертификата для ID \'%s\'</string>
<string name="msg_kc_uid_bad_local">Удаление сертификата ID с \'локальным\' флагом</string>
<string name="msg_kc_uid_bad_time">Удаление ID с отметкой времени, находящейся в будущем</string>
@@ -878,16 +1021,21 @@
<string name="msg_mf_error_subkey_missing">Попытка работы с отсутствующим доп. ключом %s!</string>
<string name="msg_mf_error_conflicting_nfc_commands">Невозможно переместить ключ в токен безопасности в той же операции, которая создаёт подпись на карте.</string>
<string name="msg_mf_error_duplicate_keytocard_for_slot">Смарт-карта поддерживает только один слот для каждого типа ключа.</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">Неподходящие флаги для ключа токена безопасности.</string>
<string name="msg_mf_master">Изменение основного сертификата</string>
+ <string name="msg_mf_notation_empty">Добавление пустого пакета примечаний</string>
+ <string name="msg_mf_notation_pin">Добавление пакета примечаний с PIN-кодом</string>
<string name="msg_mf_passphrase">Изменение пароля для связки ключей</string>
<string name="msg_mf_pin">Изменение PIN-кода карты</string>
<string name="msg_mf_admin_pin">Изменение PIN-кода администратора карты</string>
<string name="msg_mf_passphrase_key">Перешифровка доп. ключа %s с новым паролем</string>
<string name="msg_mf_passphrase_empty_retry">Ошибка установки нового пароля, пробую снова используя старый пустой пароль</string>
+ <string name="msg_mf_passphrase_fail">Пароль доп. ключа не может быть изменён! (Что его отличает от других ключей?)</string>
<string name="msg_mf_primary_replace_old">Замена сертификата ID предыдущего основного пользователя</string>
<string name="msg_mf_primary_new">Создание нового сертификата для основного идентификатора</string>
<string name="msg_mf_restricted_mode">Переход в ограниченный режим работы</string>
<string name="msg_mf_subkey_change">Изменение доп. ключа %s</string>
+ <string name="msg_mf_require_divert">Отвлечение на токен безопасности для криптографических операций</string>
<string name="msg_mf_require_passphrase">Для действий необходим пароль</string>
<string name="msg_mf_subkey_new">Добавление нового доп. ключа типа %s</string>
<string name="msg_mf_subkey_new_id">Идентификатор нового доп. ключа: %s</string>
@@ -895,12 +1043,14 @@
<string name="msg_mf_subkey_revoke">Аннулирование доп. ключа %s</string>
<string name="msg_mf_subkey_strip">Отделение доп. ключа %s</string>
<string name="msg_mf_keytocard_start">Перемещение доп. ключа %s в токен безопасности</string>
+ <string name="msg_mf_keytocard_finish">Перемещено %1$s в токен безопасности %2$s</string>
<string name="msg_mf_success">Связка успешно изменена</string>
<string name="msg_mf_uid_add">Добавление ID пользователя %s</string>
<string name="msg_mf_uid_primary">Изменение основного идентификатора на %s</string>
<string name="msg_mf_uid_revoke">Аннулирование ID пользователя %s</string>
<string name="msg_mf_uid_error_empty">Идентификатор пользователя не может быть пустой</string>
<string name="msg_mf_uat_error_empty">Атрибут пользователя не должен быть пустым!</string>
+ <string name="msg_mf_uat_add_image">Добавление пользовательского атрибута типа изображения</string>
<string name="msg_mf_uat_add_unknown">Добавление атрибута пользователя неизвестного типа</string>
<string name="msg_mf_unlock_error">Ошибка разблокирования связки!</string>
<string name="msg_mf_unlock">Разблокирование связки</string>
@@ -912,6 +1062,8 @@
<string name="msg_con_save_public">Сохранение публичной связки ключей</string>
<string name="msg_con_db_clear">Очистка базы данных</string>
<string name="msg_con_success">Успешно консолидированная база данных</string>
+ <string name="msg_con_critical_in">Ввод в критическую фазу</string>
+ <string name="msg_con_critical_out">Выход из критической фазы</string>
<string name="msg_con_delete_public">Удаление кэша связки публичных ключей</string>
<string name="msg_con_delete_secret">Удаление кэша связки секретных ключей</string>
<string name="msg_con_error_db">Ошибка открытия базы данных!</string>
@@ -922,24 +1074,46 @@
<string name="msg_con_recover">Возобновление процесса консолидации</string>
<string name="msg_con_recursive">Пропуск рекурсивной консолидации</string>
<string name="msg_con_recover_unknown">Возобновление процесса консолидации из неизвестного состояния</string>
+ <plurals name="msg_con_reimport_public">
+ <item quantity="one">Переимпортирование публичного ключа!</item>
+ <item quantity="few">Переимпортирование %d публичных ключей!</item>
+ <item quantity="many">Переимпортирование %d публичных ключей!</item>
+ <item quantity="other">Переимпортирование %d публичных ключей!</item>
+ </plurals>
<string name="msg_con_reimport_public_skip">Нет публичных ключей для повторного импорта, пропуск…</string>
+ <plurals name="msg_con_reimport_secret">
+ <item quantity="one">Переимпортирование секретного ключа!</item>
+ <item quantity="few">Переимпортирование %d секретных ключей!</item>
+ <item quantity="many">Переимпортирование %d секретных ключей!</item>
+ <item quantity="other">Переимпортирование %d секретных ключей!</item>
+ </plurals>
<string name="msg_con_reimport_secret_skip">Нет секретных ключей для повторного импорта, пропуск…</string>
+ <string name="msg_con_warn_delete_public">Исключение удаления публичного файла кэша</string>
+ <string name="msg_con_warn_delete_secret">Исключение удаления секретного файла кэша</string>
<!--Edit Key (higher level than modify)-->
<string name="msg_ed">Выполнение операции ключа</string>
<string name="msg_ed_caching_new">Кэширование нового пароля</string>
+ <string name="msg_ed_error_no_parcel">Отсутствует SaveKeyringParcel! (это ошибка, пожалуйста, сообщите о ней)</string>
<string name="msg_ed_error_key_not_found">Ключ не найден!</string>
<string name="msg_ed_error_extract_public_upload">Ошибка извлечения публичного ключа для загрузки!</string>
+ <string name="msg_ed_fetching">Извлечение ключа для изменения (%s)</string>
<string name="msg_ed_success">Операция с ключом успешна!</string>
<!--Promote key-->
<string name="msg_pr_error_key_not_found">Ключ не найден!</string>
+ <string name="msg_pr_fetching">Извлечение ключа для изменения (%s)</string>
+ <string name="msg_pr_subkey_nomatch">Доп. ключ не в токене безопасности: %s</string>
<!--Other messages used in OperationLogs-->
+ <string name="msg_ek_error_dummy">Невозможно редактировать связку с отделённым основным ключом!</string>
<string name="msg_ek_error_not_found">Ключ не найден!</string>
<!--Messages for DecryptVerify operation-->
<string name="msg_dc_askip_bad_flags">Ключ не является ключом шифрования, пропуск...</string>
<string name="msg_dc_askip_unavailable">Ключ недоступен, пропуск…</string>
<string name="msg_dc_askip_no_key">Данные зашифрованы неизвестным ключом, пропуск...</string>
<string name="msg_dc_askip_not_allowed">Данные не зашифрованы допустимым ключом, пропуск...</string>
- <string name="msg_dc_clear_data">Обработка символьных данных</string>
+ <string name="msg_dc_asym">Найден блок асимметрично зашифрованных данных для ключа %s</string>
+ <string name="msg_dc_charset">Найден заголовок кодировки: \'%s\'</string>
+ <string name="msg_dc_backup_version">Найден заголовок backupVersion: \'%s\'</string>
+ <string name="msg_dc_clear_data">Обработка текстовых данных</string>
<string name="msg_dc_clear_decompress">Распаковка сжатых данных</string>
<string name="msg_dc_clear_meta_file">Имя файла: %s</string>
<string name="msg_dc_clear_meta_mime">MIME-тип: %s</string>
@@ -961,7 +1135,9 @@
<string name="msg_dc_error_no_data">Зашифрованные данные не найдены!</string>
<string name="msg_dc_error_no_key">Зашифрованные данные с известным приватным ключом не найдены!</string>
<string name="msg_dc_error_no_signature">Отсутствуют данные подписи!</string>
+ <string name="msg_dc_error_pgp_exception">Произошла ошибка во время работы OpenPGP!</string>
<string name="msg_dc_integrity_check_ok">Проверка целостности пройдена!</string>
+ <string name="msg_dc_ok_meta_only">Были запрошены только метаданные без расшифровки</string>
<string name="msg_dc_ok">Расшифрование/проверка закончена</string>
<string name="msg_dc_pass_cached">Применяю кэшированный пароль</string>
<string name="msg_dc_pending_nfc">Требуется NFC-токен, жду действий пользователя...</string>
@@ -970,10 +1146,21 @@
<string name="msg_dc">Расшифровка началась...</string>
<string name="msg_dc_sym_skip">Симметричные данные не допускаются, пропуск...</string>
<string name="msg_dc_sym">Обнаружена симметрично зашифрованная информация</string>
+ <string name="msg_dc_trail_asym">Встречены замыкающие, асимметрично зашифрованные данные для ключа %s</string>
+ <string name="msg_dc_trail_sym">Встречены замыкающие, симметрично зашифрованные данные</string>
+ <string name="msg_dc_trail_unknown">Встречены замыкающие данные неизвестного типа</string>
<string name="msg_dc_unlocking">Разблокировка секретного ключа</string>
+ <string name="msg_dc_insecure_encryption_key">Был использован небезопасной ключ шифрования! Это могло произойти из-за устаревания ключа или из-за атаки.</string>
+ <string name="msg_dc_insecure_symmetric_encryption_algo">Был использован небезопасной алгоритм шифрования! Это могло произойти из-за устаревания приложения или из-за атаки.</string>
+ <string name="msg_dc_insecure_hash_algo">Был использован небезопасной алгоритм хэша! Это могло произойти из-за устаревания приложения или из-за атаки.</string>
+ <string name="msg_dc_insecure_mdc_missing">Отсутствует пакет кода обнаружения модификации (MDC)! Это могло произойти из-за устаревшего приложения шифрования или из-за атаки.</string>
+ <string name="msg_dc_insecure_key">Небезопасный ключ: либо битовая длина RSA/DSA/ElGamal слишком мала, либо алгоритм ECC считается небезопасным! Это могло произойти из-за устаревания приложения или из-за атаки.</string>
<!--Messages for VerifySignedLiteralData operation-->
<string name="msg_vl">Начинается проверка подписи</string>
+ <string name="msg_vl_error_no_siglist">Нет списка подписей в подписанных текстовых данных!</string>
+ <string name="msg_vl_error_wrong_key">Сообщение не подписано ожидаемым ключом!</string>
<string name="msg_vl_error_no_signature">Отсутствуют данные подписи!</string>
+ <string name="msg_vl_error_missing_literal">Нет полезной нагрузки в подписанных текстовых данных</string>
<string name="msg_vl_clear_meta_file">Имя файла: %s</string>
<string name="msg_vl_clear_meta_mime">MIME-тип: %s</string>
<string name="msg_vl_clear_meta_time">Время изменения: %s</string>
@@ -999,18 +1186,33 @@
<string name="msg_pse_error_key_sign">Выбранным ключом подписи нельзя подписать данные! </string>
<string name="msg_pse_error_sign_key">Ошибка при выборке ключа подписи!</string>
<string name="msg_pse_error_nfc">Ошибка данных NFC!</string>
+ <string name="msg_pse_error_no_passphrase">Пароль не предусмотрен!</string>
<string name="msg_pse_error_pgp">Внутренняя ошибка OpenPGP!</string>
+ <string name="msg_pse_error_sig">Обнаружена исключительная ситуация подписи OpenPGP!</string>
+ <string name="msg_pse_error_unlock">Неизвестная ошибка разблокировения ключа!</string>
<string name="msg_pse_key_ok">Шифрование для ключа: %s</string>
+ <string name="msg_pse_key_unknown">Отсутствует ключ для шифрования: %s</string>
+ <string name="msg_pse_key_warn">Некорректный ключ для шифрования: %s</string>
<string name="msg_pse_ok">Подпись/Шифрование успешно произведены!</string>
<string name="msg_pse_pending_nfc">Требуется NFC-токен, жду действий пользователя...</string>
<string name="msg_pse_pending_passphrase">Требуется пароль, жду действий пользователя...</string>
+ <string name="msg_pse_signing">Подписывание данных (без шифрования)</string>
+ <string name="msg_pse_signing_detached">Создание отделённой подписи</string>
+ <string name="msg_pse_sigcrypting">Шифрование данных с подписыванием</string>
<string name="msg_pse">Подпись и/или шифрование начаты</string>
<string name="msg_pse_symmetric">Подготовка симметричного шифрования</string>
<string name="msg_crt_certifying">Генерация сертификатов</string>
+ <plurals name="msg_crt_certify_uids">
+ <item quantity="one">Сертификация ID пользователя ключа %2$s</item>
+ <item quantity="few">Сертификация %1$d ID пользователей ключа %2$s</item>
+ <item quantity="many">Сертификация %1$d ID пользователей ключа %2$s</item>
+ <item quantity="other">Сертификация %1$d ID пользователей ключа %2$s</item>
+ </plurals>
<string name="msg_crt_error_master_not_found">Основной ключ не найден!</string>
<string name="msg_crt_error_nothing">Нет сертифицированных ключей!</string>
<string name="msg_crt_error_unlock">Ошибка разблокирования основного ключа!</string>
<string name="msg_crt">Сертификация связок</string>
+ <string name="msg_crt_master_fetch">Получение сертификации основного ключа</string>
<string name="msg_crt_nfc_return">Возвращение к экрану NFC</string>
<string name="msg_crt_save">Сохранение сертифицированного ключа %s</string>
<string name="msg_crt_saving">Сохранение связки</string>
@@ -1019,54 +1221,220 @@
<string name="msg_crt_warn_not_found">Ключ не найден!</string>
<string name="msg_crt_warn_cert_failed">Создание сертификата не удалось!</string>
<string name="msg_crt_warn_save_failed">Ошибка операции сохранения!</string>
+ <string name="msg_crt_warn_upload_failed">Операция загрузки не удалась!</string>
<string name="msg_crt_upload_success">Ключ успешно загружен на сервер</string>
+ <plurals name="msg_import">
+ <item quantity="one">Импортирование ключа</item>
+ <item quantity="few">Импортирование %d ключей</item>
+ <item quantity="many">Импортирование %d ключей</item>
+ <item quantity="other">Импортирование %d ключей</item>
+ </plurals>
+ <string name="msg_import_fetch_error_decode">Ошибка декодирования извлечённой связки!</string>
+ <string name="msg_import_fetch_error">Ключ не может быть получен! (Проблемы с сетью?)</string>
+ <string name="msg_import_fetch_error_keyserver">Не удалось получить ключ с серверов ключей: %s</string>
+ <string name="msg_import_fetch_error_keyserver_secret">Невозможно импортировать секретный ключ с сервера ключей!</string>
+ <string name="msg_import_fetch_keybase">Извлечение с keybase.io: %s</string>
+ <string name="msg_import_fetch_facebook">Извлечение с Facebook: %s</string>
+ <string name="msg_import_fetch_keyserver">Извлечение с сервера ключей: %s</string>
+ <string name="msg_import_fetch_keyserver_ok">Ключ успешно извлечён</string>
+ <string name="msg_import_keyserver">Используется сервер ключей %s</string>
+ <string name="msg_import_merge">Объединение полученных данных</string>
+ <string name="msg_import_merge_error">Ошибка объединения полученных данных!</string>
<string name="msg_import_error">Ошибка операции импорта!</string>
<string name="msg_import_error_io">Операция импорта прервана из-за ошибки ввода/вывода!</string>
<string name="msg_import_partial">Операция импорта успешна, но с ошибками!</string>
<string name="msg_import_success">Операция импорта успешна!</string>
+ <plurals name="msg_backup">
+ <item quantity="one">Резервное копирование с одним ключом</item>
+ <item quantity="few">Резервное копирование с %d ключами</item>
+ <item quantity="many">Резервное копирование с %d ключами</item>
+ <item quantity="other">Резервное копирование с %d ключами</item>
+ </plurals>
+ <string name="msg_backup_all">Резервное копирование со всеми ключами</string>
+ <string name="msg_backup_public">Создание резервной копии публичного ключа %s</string>
+ <string name="msg_backup_secret">Создание резервной копии секретного ключа %s</string>
+ <string name="msg_backup_error_uri_open">Ошибка при открытии URL-потока!</string>
<string name="msg_backup_error_db">Ошибка базы данных!</string>
<string name="msg_backup_error_io">Ошибка ввода/вывода!</string>
+ <string name="msg_backup_success">Операция резервного копирования успешно завершена</string>
+ <string name="msg_upload">Загрузка публичного ключа</string>
+ <string name="msg_upload_proxy_direct">Прокси: нет</string>
+ <string name="msg_upload_proxy_tor">Прокси: TOR</string>
+ <string name="msg_upload_proxy">Прокси: %s</string>
<string name="msg_upload_server">Сервер: %s</string>
<string name="msg_upload_key">ID ключа: %s</string>
+ <string name="msg_upload_error_key">Ошибка предварительной обработки данных ключа!</string>
<string name="msg_upload_error_not_found">Ключ не найден!</string>
+ <string name="msg_upload_error_upload">Ошибка загрузки ключа на сервер! Пожалуйста, проверьте подключение к интернету.</string>
+ <string name="msg_upload_success">Загрузка на сервер ключей успешно завершена</string>
<string name="msg_del_error_empty">Нет данных для удаления!</string>
<string name="msg_del_error_multi_secret">Секретные ключи можно удалять только по одному!</string>
+ <plurals name="msg_del">
+ <item quantity="one">Удаление одного ключа</item>
+ <item quantity="few">Удаление %d ключей</item>
+ <item quantity="many">Удаление %d ключей</item>
+ <item quantity="other">Удаление %d ключей</item>
+ </plurals>
+ <string name="msg_del_key">Удаление ключа %s</string>
+ <string name="msg_del_key_fail">Ошибка удаления ключа %s</string>
+ <string name="msg_del_consolidate">Консолидация базы данных после удаления секретного ключа</string>
+ <plurals name="msg_del_ok">
+ <item quantity="one">Успешно удалён ключ</item>
+ <item quantity="few">Успешно удалено %d ключа</item>
+ <item quantity="many">Успешно удалено %d ключей</item>
+ <item quantity="other">Успешно удалено %d ключей</item>
+ </plurals>
+ <plurals name="msg_del_fail">
+ <item quantity="one">Не удалось удалить один ключ</item>
+ <item quantity="few">Не удалось удалить %d ключа</item>
+ <item quantity="many">Не удалось удалить %d ключей</item>
+ <item quantity="other">Не удалось удалить %d ключей</item>
+ </plurals>
+ <string name="msg_revoke_error_empty">Нечего аннулировать!</string>
+ <string name="msg_revoke_error_not_found">Не удаётся найти ключ для аннулирования!</string>
+ <string name="msg_revoke_key">Аннулирование ключа %s</string>
+ <string name="msg_revoke_key_fail">Ошибка при аннулировании ключа</string>
+ <string name="msg_revoke_ok">Ключ успешно аннулирован</string>
<!--Linked Identity verification-->
+ <string name="msg_lv">Проверка связанного идентификатора…</string>
<string name="msg_lv_match">Поиск токена</string>
+ <string name="msg_lv_match_error">Токен не найден в ресурсе!</string>
+ <string name="msg_lv_fp_ok">Отпечатки совпадают.</string>
+ <string name="msg_lv_fp_error">Несоответствие отпечатков!</string>
+ <string name="msg_lv_error_twitter_auth">Ошибка при получении токена аутентификации Twitter!</string>
+ <string name="msg_lv_error_twitter_handle">Дескриптор учётной записи Twitter не соответствует ответному!</string>
+ <string name="msg_lv_error_twitter_response">Неожиданный ответ от API сервиса Twitter!</string>
+ <string name="msg_lv_error_github_handle">Дескриптор учётной записи GitHub не соответствует ответному!</string>
+ <string name="msg_lv_fetch">URL извлечения \'%s\'</string>
+ <string name="msg_lv_fetch_redir">Следование перенаправлению на \'%s\'</string>
+ <string name="msg_lv_fetch_ok">Успешно извлечено (HTTP %s)</string>
+ <string name="msg_lv_fetch_error">Ошибка сервера (HTTP %s)</string>
+ <string name="msg_lv_fetch_error_url">URL-адрес некорректен!</string>
+ <string name="msg_lv_fetch_error_io">Ошибка ввода-вывода!</string>
+ <string name="msg_lv_fetch_error_format">Ошибка формата!</string>
+ <string name="msg_lv_fetch_error_nothing">Ресурс не найден!</string>
+ <string name="msg_bench">Анализ быстродействия некоторых операций…</string>
+ <string name="msg_bench_enc_time">Время шифрования: %ss</string>
+ <string name="msg_bench_enc_time_avg">Среднее время шифрования 5МБ: %ss</string>
+ <string name="msg_bench_dec_time">Время расшифрования: %ss</string>
+ <string name="msg_bench_dec_time_avg">Среднее время расшифрования 5МБ: %ss</string>
+ <string name="msg_bench_s2k_100ms_its">Количество итераций S2K за 100 мс: %s</string>
+ <string name="msg_bench_s2k_for_it">Время для %1$s SHA1 S2K итераций: %2$sms</string>
+ <string name="msg_bench_success">Анализ быстродействия завершён!</string>
+ <string name="msg_data">Обработка входных данных</string>
+ <string name="msg_data_openpgp">Попытка обработки данных OpenPGP</string>
+ <string name="msg_data_detached">Встреченная отделённая подпись</string>
+ <string name="msg_data_detached_clear">Удаление прежних неподписанных данных!</string>
+ <string name="msg_data_detached_sig">Обработка отделённой подписи</string>
+ <string name="msg_data_detached_raw">Обработка подписанных данных</string>
+ <string name="msg_data_detached_nested">Пропуск встроенных подписанных данных!</string>
+ <string name="msg_data_detached_trailing">Пропуск замыкающих данных после подписанной части!</string>
+ <string name="msg_data_detached_unsupported">Неподдерживаемый тип отделённой подписи!</string>
+ <string name="msg_data_error_io">Ошибка чтения входных данных!</string>
+ <string name="msg_data_mime_bad">Невозможно разобрать как MIME-данные</string>
+ <string name="msg_data_mime_filename">Имя файла: \'%s\'</string>
+ <string name="msg_data_mime_from_extension">Подбор MIME-типа по расширению</string>
+ <string name="msg_data_mime_length">Длина: %s</string>
+ <string name="msg_data_mime_charset">Кодировка \'%s\'</string>
+ <string name="msg_data_mime_charset_faulty">Кодировка \'%s\', но декодирование не удалось!</string>
+ <string name="msg_data_mime_charset_guess">Кодировка, по-видимому, \'%s\'</string>
+ <string name="msg_data_mime_charset_unknown">Кодировка неизвестна или данные не являются текстом.</string>
+ <string name="msg_data_mime">Анализ структуры данных MIME</string>
+ <string name="msg_data_mime_ok">Анализ окончен</string>
+ <string name="msg_data_mime_none">MIME-структура не найдена</string>
+ <string name="msg_data_mime_part">Обработка MIME-части</string>
+ <string name="msg_data_mime_type">Тип: %s</string>
+ <string name="msg_data_ok">Обработка данных успешно завершена</string>
+ <string name="msg_data_skip_mime">Пропуск анализа MIME</string>
<string name="msg_acc_saved">Аккаунт сохранен</string>
+ <string name="msg_get_success">Скачано успешно!</string>
+ <string name="msg_get_file_not_found">Входной файл не найден!</string>
+ <string name="msg_get_no_valid_keys">Не найдено ключей в файле / буфере обмена!</string>
+ <string name="msg_get_too_many_responses">Запрос на поиск ключа вернул слишком много кандидатов. Уточните ваш запрос!</string>
+ <string name="msg_get_query_too_short">Слишком короткий запрос для поиска. Уточните ваш запрос!</string>
+ <string name="msg_get_query_too_short_or_too_many_responses">Либо ключей не найдено, либо найдено слишком много. Пожалуйста, уточните запрос!</string>
+ <string name="msg_download_query_failed">При поиске ключей произошла ошибка.</string>
<!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_verification">Попытки проверки базы ключей для %s</string>
+ <string name="msg_keybase_error_no_prover">Не найдено проверки доказательства для %s</string>
+ <string name="msg_keybase_error_fetching_evidence">Проблема с извлечением доказательства</string>
+ <string name="msg_keybase_error_key_mismatch">Отпечаток ключа не совпадает с опубликованным доказательством</string>
+ <string name="msg_keybase_error_specific">%s</string>
+ <string name="msg_keybase_error_msg_payload_mismatch">Расшифрованное опубликованное доказательство не совпадает с ожидаемым значением</string>
<!--Messages for Mime parsing operation-->
+ <string name="msg_mime_parsing_start">Анализ MIME-структуры</string>
+ <string name="msg_mime_parsing_error">Анализ MIME завершился неудачей</string>
+ <string name="msg_mime_parsing_success">Анализ MIME успешно завершён!</string>
<!--PassphraseCache-->
+ <string name="passp_cache_notif_touch_to_clear">Коснитесь, чтобы очистить пароли.</string>
+ <plurals name="passp_cache_notif_n_keys">
+ <item quantity="one">%d пароль запомнен</item>
+ <item quantity="few">%d пароля запомнено</item>
+ <item quantity="many">%d паролей запомнено</item>
+ <item quantity="other">%d паролей запомнено</item>
+ </plurals>
+ <string name="passp_cache_notif_keys">Запомненные пароли</string>
+ <string name="passp_cache_notif_clear">Очистить пароли</string>
<string name="passp_cache_notif_pwd">Пароль</string>
<!--Keyserver sync-->
+ <string name="keyserver_sync_orbot_notif_title">Синхронизация с серверами требует Orbot</string>
+ <string name="keyserver_sync_orbot_notif_msg">Коснитесь, чтобы запустить Orbot</string>
+ <string name="keyserver_sync_orbot_notif_start">Запустить Orbot</string>
+ <string name="keyserver_sync_orbot_notif_ignore">Напрямую</string>
<!--First Time-->
<string name="first_time_text1">Верните вашу приватность с помощью OpenKeychain!</string>
<string name="first_time_create_key">Создать свой ключ</string>
<string name="first_time_import_key">Импорт ключа из файла</string>
<string name="first_time_security_token">Использовать токен безопасности</string>
+ <string name="first_time_security_token_subtitle">(Fidesmo, YubiKey NEO, SIGILANCE, …)</string>
<string name="first_time_skip">Пропустить настройку</string>
<string name="first_time_blank_security_token_yes">Использовать этот токен безопасности</string>
+ <string name="backup_text">Резервные копии, которые включают в себя ваши собственные ключи, никогда не должны использоваться совместно с другими людьми!</string>
+ <string name="backup_all">Все ключи + ваши собственные ключи</string>
+ <string name="backup_public_keys">Все ключи</string>
+ <string name="backup_section">Резервное копирование</string>
+ <string name="restore_section">Восстановление</string>
<!--unsorted-->
<string name="section_certifier_id">Кем подписан</string>
<string name="section_cert">Детали сертификации</string>
<string name="label_user_id">Идентификатор</string>
+ <string name="unknown_uid"><![CDATA[<неизвестно>]]></string>
<string name="empty_certs">Этот ключ не сертифицирован</string>
+ <string name="certs_text">Здесь отображаются только проверенные само-сертификаты и проверенные сертификаты, созданные с вашими ключами.</string>
+ <string name="section_uids_to_certify">Идентификаторы для</string>
+ <string name="certify_text">Импортируемые ключи содержат “идентификаторы”: имена и адреса электронной почты. Выберите для подтверждения только те, которые соответствуют вашим ожиданиям.</string>
+ <string name="certify_fingerprint_text">Сравните отпечаток, символ за символом, с тем, что отображается на устройстве вашего партнёра.</string>
+ <string name="certify_fingerprint_text_phrases">Сравните эти фразы с теми, что отображается на устройстве вашего партнёра.</string>
<string name="label_revocation">Причина отзыва</string>
<string name="label_cert_type">Тип</string>
<string name="error_key_not_found">Ключ не найден!</string>
<string name="error_key_processing">Ошибка обработки ключа!</string>
+ <string name="key_stripped">отделено</string>
+ <string name="key_no_passphrase">без пароля</string>
<string name="key_unavailable">недоступно</string>
+ <string name="secret_cannot_multiple">Ваши собственные ключи можно удалять только по одному!</string>
<string name="title_view_cert">Просмотреть детали сертификации</string>
<string name="unknown_algorithm">неизв.</string>
<string name="can_sign_not">не для подписания</string>
<string name="error_no_encrypt_subkey">Нет доп. ключа для шифрования!</string>
<string name="contact_show_key">Показать ключ (%s)</string>
+ <string name="swipe_to_update">Проведите вниз для обновления с сервера ключей</string>
<string name="error_no_file_selected">Выберите хотя бы один файл для шифрования!</string>
+ <string name="error_multi_files">Сохранение нескольких файлов не поддерживается. Это ограничение текущей версии Android.</string>
+ <string name="error_multi_clipboard">Шифрование нескольких файлов в буфер обмена не поддерживается.</string>
+ <string name="error_detached_signature">Только подписывание бинарных файлов не поддерживается, выберите по крайней мере один ключ шифрования.</string>
<string name="error_empty_text">Введите текст для шифрования!</string>
<string name="error_log_share_internal">Внутренняя ошибка при подготовке журнала!</string>
<string name="key_colon">Ключ:</string>
+ <string name="exchange_description">Для того, чтобы начать обмен ключами, выбрать количество участников на правой стороне, а затем нажмите кнопку \"Начать обмен\".\n\nВам будет предложено ещё два вопроса, чтобы убедиться, что только требуемые участники в обмене и их отпечатки являются правильными.</string>
<string name="btn_start_exchange">Начать обмен</string>
+ <string name="user_id_none"><![CDATA[<нет>]]></string>
<!--Android Account-->
+ <string name="account_no_manual_account_creation">Вы не можете создавать аккаунты OpenKeychain вручную.</string>
+ <string name="account_privacy_title">Конфиденциальность</string>
+ <string name="account_privacy_text">OpenKeychain не синхронизирует ваши контакты с интернетом, а только связывает их с ключами, основываясь на именах и адресах электронной почты. Это делает ваше устройство автономным.</string>
+ <string name="sync_notification_permission_required_title">Требуется доступ к контактам</string>
+ <string name="sync_notification_permission_required_text">Коснитесь, чтобы настроить ссылки на контакты</string>
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">Выберите метод разблокировки</string>
@@ -1078,25 +1446,46 @@
<string name="passphrases_match">Пароли совпадают</string>
<string name="passphrase_saved">Пароль сохранён</string>
<string name="passphrase_invalid">Неверный пароль</string>
+ <string name="missing_passphrase">Отсутствует пароль</string>
<string name="passphrase_again">Еще раз</string>
<string name="lockpattern">Рисунок блокировки</string>
<string name="lockpatternNFC">NFC + рисунок блокировки</string>
<string name="unlock_method">Метод разблокировки</string>
<string name="set_passphrase">Задать пароль</string>
+ <string name="draw_lockpattern">Начертите рисунок блокировки</string>
<string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
+ <string name="nfc_wrong_tag">Неправильный тег. Пожалуйста, попробуйте ещё раз.</string>
+ <string name="enable_nfc">Пожалуйста, включите NFC в настройках</string>
+ <string name="no_nfc_support">Это устройство не поддерживает NFC</string>
+ <string name="nfc_write_successful">Успешно записано на NFC-тег</string>
<string name="unlocked">Разблокирован</string>
<string name="nfc_settings">Настройки</string>
<string name="snack_security_token_view">Просмотр</string>
<string name="snack_security_token_import">Импорт</string>
+ <string name="button_bind_key">Привязать ключ</string>
<string name="security_token_serial_no">Серийный номер: %s</string>
- <string name="security_token_key_holder">Владелец ключа:</string>
+ <string name="security_token_key_holder">Владелец ключа: %s</string>
+ <string name="security_token_key_holder_not_set"><![CDATA[Держатель ключа: <не задан>]]></string>
+ <string name="security_token_status_bound">Токены безопасности совпали и привязаны к ключу</string>
+ <string name="security_token_status_unbound">Токены безопасности совпали, могут быть привязаны к ключу</string>
+ <string name="security_token_status_partly">Токены безопасности совпали, частично привязаны к ключу</string>
+ <string name="security_token_create">Держите токен безопасности возле задней части вашего устройства.</string>
<string name="security_token_reset_or_import">Этот токен безопасности уже содержит ключ. Вы можете импортировать ключ через облако или сбросить токен безопасности.</string>
<string name="btn_import">Импорт</string>
<string name="btn_reset">Сброс</string>
<string name="security_token_import_radio">Импортировать ключ</string>
<string name="security_token_reset_radio">Сбросить токен безопасности</string>
+ <string name="security_token_reset_warning">Сброс токена безопасности полностью уничтожает ключи на нём. После этого вы не сможете расшифровать сообщения или файлы, зашифрованные с помощью данных ключей!</string>
+ <string name="snack_security_token_other">На токене безопасности хранится другой ключ!</string>
<string name="security_token_error">Ошибка: %s</string>
+ <plurals name="security_token_error_pin">
+ <item quantity="one">Неправильный PIN-код!\nОсталась %d попытка.</item>
+ <item quantity="few">Неправильный PIN-код!\nОсталось %d попытки.</item>
+ <item quantity="many">Неправильный PIN-код!\nОсталось %d попыток.</item>
+ <item quantity="other">Неправильный PIN-код!\nОсталось %d попыток.</item>
+ </plurals>
+ <string name="security_token_error_terminated">Токен безопасности в состоянии завершения.</string>
<string name="security_token_error_wrong_length">Введённый PIN-код слишком короткий. Коды должны быть не менее 6 цифр.</string>
<string name="security_token_error_conditions_not_satisfied">Условия использования неудовлетворительные.</string>
<string name="security_token_error_security_not_satisfied">Статус безопасности неудовлетворительный.</string>
@@ -1105,6 +1494,8 @@
<string name="security_token_error_unknown">Неизвестная ошибка</string>
<string name="security_token_error_bad_data">Токен безопасности сообщил о повреждённых данных.</string>
<string name="security_token_error_chaining_error">Токен безопасности ожидал последней команды в цепочке.</string>
+ <string name="security_token_error_header">Токен безопасности сообщил о повреждённом %s байте.</string>
+ <string name="security_token_error_iso_dep_not_supported">Тег не поддерживает ISO-DEP (ISO 14443-4)</string>
<string name="security_token_error_try_again">Попробовать ещё раз</string>
<string name="btn_delete_original">Удалить оригинальный файл</string>
<string name="snack_encrypt_filenames_on">Имена файлов <b>шифруются</b>.</string>
@@ -1137,17 +1528,43 @@
<string name="error_scan_fp">Ошибка сканирования отпечатка!</string>
<string name="error_scan_match">Отпечатки не совпадают!</string>
<string name="error_expiry_past">Дата истечения срока действия в прошлом!</string>
+ <string name="linked_create_https_1_1">Создавая связанный идентификатор данного типа, можно привязать свой ключ к управляемому вами веб-сайту.</string>
+ <string name="linked_create_https_1_2">Чтобы сделать это, вы публикуете текстовый файл на данном сайте, затем создаете связанный идентификатор, который привязывается к нему.</string>
+ <string name="linked_create_https_1_3">Пожалуйста, введите URL, где вы сможете разместить текстовый файл доказательства. Обратите внимание, что ваш сервер должен поддерживать протокол HTTPS и иметь действительный сертификат TLS!</string>
<string name="linked_create_https_1_4">Например: https://example.com/pgpkey.txt</string>
+ <string name="linked_create_https_created">Файл доказательства был создан. Для следующего шага вы должны сохранить и загрузить этот файл на указанный вами URL:</string>
+ <string name="linked_create_https_2_1">Файл доказательства для этого URL был создан:</string>
<string name="linked_create_https_2_2">Для следующего шага вы должны сохранить и загрузить этот файл.</string>
+ <string name="linked_create_https_2_3">Убедитесь, что файл доступен по правильному URL, затем проверьте настройки.</string>
+ <string name="linked_create_https_2_4">После успешной проверки нажмите кнопку Готово, чтобы добавить связанный идентификатор в вашу связку и завершить процесс.</string>
+ <string name="linked_create_twitter_1_1">Создавая связанный идентификатор данного типа, можно привязать свой ключ к управляемой вами учётной записи Twitter.</string>
+ <string name="linked_create_twitter_1_2">Чтобы сделать это, вам необходимо опубликовать специфический твит на своей ленте, а затем создать связанный идентификатор, ссылающийся на данный твит.</string>
<string name="linked_create_twitter_1_3">Пожалуйста, введите свой псевдоним в Twitter, чтобы продолжить.</string>
+ <string name="linked_create_twitter_handle">Обработчик Twitter</string>
+ <string name="linked_create_twitter_2_1">Нажмите любую кнопку, чтобы твитнуть сообщение!</string>
+ <string name="linked_create_twitter_2_2">Вы можете редактировать твит перед отправкой, пока текст в скобках не изменён.</string>
+ <string name="linked_create_twitter_2_3">Как только ваш твит будет опубликован как &lt;b&gt;@%s&lt;/b&gt;, нажмите кнопку Проверить для сканирования ленты.</string>
+ <string name="linked_create_twitter_2_4">После успешной проверки нажмите кнопку Готово, чтобы добавить связанный идентификатор в вашу связку и завершить процесс.</string>
<string name="linked_create_verify">Проверить</string>
<string name="linked_text_clipboard">Текст был скопирован в буфер обмена</string>
+ <string name="linked_verified_https">Связь между этим веб-сайтом и ключом была надёжно проверена. <b>Если вы считаете, что сайт является подлинным</b>, подтвердите эту проверку своим ключом.</string>
+ <string name="linked_verified_github">Связь между этим аккаунтом GitHub и ключом была надёжно проверена. <b>Если вы считаете, что аккаунт является подлинным</b>, подтвердите эту проверку своим ключом.</string>
+ <string name="linked_verified_dns">Связь между этим доменным именем и ключом была надёжно проверена. <b>Если вы считаете, что домен является подлинным</b>, подтвердите эту проверку своим ключом.</string>
+ <string name="linked_verified_twitter">Связь между этим аккаунтом Twitter и ключом была надёжно проверена. <b>Если вы считаете, что аккаунт является подлинным</b>, подтвердите эту проверку своим ключом.</string>
<string name="linked_verified_secret_https">Всё выглядит в порядке.</string>
<string name="linked_verified_secret_github">Всё выглядит в порядке.</string>
<string name="linked_verified_secret_dns">Всё выглядит в порядке.</string>
<string name="linked_verified_secret_twitter">Всё выглядит в порядке.</string>
+ <plurals name="linked_id_expand">
+ <item quantity="one">Здесь ещё один неизвестный тип идентификатора</item>
+ <item quantity="few">Здесь ещё %d неизвестных типа идентификаторов</item>
+ <item quantity="many">Здесь ещё %d неизвестных типов идентификаторов</item>
+ <item quantity="other">Здесь ещё %d неизвестных типов идентификаторов</item>
+ </plurals>
<!--Other Linked Identity strings-->
+ <string name="linked_select_1">\'Связывание идентификатора\' привязывает ваш PGP-ключ к ресурсу в интернете.</string>
<string name="linked_select_2">Пожалуйста, выберите тип:</string>
+ <string name="linked_id_generic_text">Этот файл требует владельца OpenPGP-ключа с полным ID %2$s.\n\nТокен для доказательства:\n%1$s</string>
<string name="linked_verifying">Проверка…</string>
<string name="linked_verify_success">Проверено!</string>
<string name="linked_verify_error">Ошибка проверки!</string>
@@ -1158,7 +1575,9 @@
<string name="btn_finish">Завершить</string>
<string name="linked_title_https">Сайт (HTTPS)</string>
<string name="linked_title_dns">Доменное имя (DNS)</string>
- <string name="linked_title_twitter">Твиттер</string>
+ <string name="linked_title_github">GitHub</string>
+ <string name="linked_title_twitter">Twitter</string>
+ <string name="card_linked_identity">Связанный идентификатор</string>
<string name="linked_button_verify">Проверить</string>
<string name="linked_button_retry">Повторить</string>
<string name="linked_button_retry_step">Повторить последний шаг</string>
@@ -1167,6 +1586,9 @@
<string name="linked_text_verifying">Проверка…</string>
<string name="linked_text_error">Ошибка</string>
<string name="linked_text_confirming">Подтверждение…</string>
+ <string name="linked_ids_more_unknown">Ещё %d неизвестных типов идентификаторов</string>
+ <string name="title_linked_id_create">Создать связанный идентификатор</string>
+ <string name="linked_github_text">Эта операция связывает ключ с вашей учётной записью GitHub.\nПросто нажмите кнопку для продолжения.</string>
<string name="linked_progress_auth_github">Авторизация при помощи GitHub...</string>
<string name="linked_progress_update_key">Обновить ключ...</string>
<string name="linked_button_start">Ссылка на аккаунт GitHub</string>
@@ -1175,6 +1597,8 @@
<string name="linked_error_network">Ошибка сети!</string>
<string name="linked_error_http">Коммуникационная ошибка: %s</string>
<string name="linked_webview_title_github">Авторизация GitHub</string>
+ <string name="linked_gist_description">Связанный идентификатор OpenKeychain</string>
+ <string name="linked_empty">Привяжите свой ключ к GitHub, Twitter или другому сайту!</string>
<string name="snack_btn_overwrite">Перезаписать</string>
<string name="backup_code_explanation">Резервная копия будет защищена кодом. Запишите его, прежде чем продолжить!</string>
<string name="backup_code_enter">Пожалуйста, введите резервный код:</string>
@@ -1195,6 +1619,7 @@
<string name="share_log_dialog_share_button">Отправить...</string>
<string name="share_log_dialog_cancel_button">Отмена</string>
<string name="toast_wrong_mimetype">Неправильный тип данных, ожидался текст!</string>
+ <string name="toast_no_text">В публикуемых данных нет текста!</string>
<string name="menu_uids_save">Сохранить</string>
<string name="title_edit_identities">Изменить соответствия</string>
<string name="title_edit_subkeys">Изменить доп. ключи</string>
diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml
index 33dfe40a7..943add07f 100644
--- a/OpenKeychain/src/main/res/values-sl/strings.xml
+++ b/OpenKeychain/src/main/res/values-sl/strings.xml
@@ -139,19 +139,9 @@
<string name="choice_4hours">4 ure</string>
<string name="choice_8hours">8 ur</string>
<string name="choice_forever">za vedno</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Odpri...</string>
<string name="error">Napaka</string>
<string name="error_message">Napaka: %s</string>
- <!--key flags-->
- <string name="flag_certify">Overi</string>
- <string name="flag_sign">Podpiši</string>
- <string name="flag_encrypt">Šifriraj</string>
- <string name="flag_authenticate">Preveri avtentičnost</string>
<!--sentences-->
<string name="no_filemanager_installed">Nimate nameščenega združljivega upravljalnika datotek.</string>
<string name="pin_for">Vnesite PIN kodo za \'%s\'</string>
@@ -473,7 +463,6 @@
<string name="edit_key_edit_user_id_revoked">Identiteta je bila preklicana. Ta poteza ne more biti razveljavljena.</string>
<string name="edit_key_edit_subkey_title">Izberite dejanje!</string>
<string name="edit_key_new_subkey">nov podključ</string>
- <string name="edit_key_select_flag">Izberite vsaj eno oznako!</string>
<string name="edit_key_error_add_identity">Dodajte vsaj eno identiteto!</string>
<string name="edit_key_error_add_subkey">Dodajte vsaj en podključ!</string>
<!--Create key-->
diff --git a/OpenKeychain/src/main/res/values-sr/strings.xml b/OpenKeychain/src/main/res/values-sr/strings.xml
index b267557f4..515381bc3 100644
--- a/OpenKeychain/src/main/res/values-sr/strings.xml
+++ b/OpenKeychain/src/main/res/values-sr/strings.xml
@@ -13,6 +13,7 @@
<string name="title_preferences">Поставке</string>
<string name="title_api_registered_apps">Апликације</string>
<string name="title_key_server_preference">ОпенПГП сервери кључева</string>
+ <string name="title_cache_ttl_preference">Прилагођавање избора „памћења“</string>
<string name="title_change_passphrase">Измена лозинке</string>
<string name="title_share_fingerprint_with">Подели отисак помоћу…</string>
<string name="title_share_key">Подели кључ помоћу…</string>
@@ -86,6 +87,7 @@
<string name="btn_add_files">Додај фајл(ове)</string>
<string name="btn_share_decrypted_text">Дели</string>
<string name="btn_open_with">Отвори помоћу…</string>
+ <string name="btn_copy_decrypted_text">Копирај на клипборд</string>
<string name="btn_decrypt_clipboard">Учитај са клипборда</string>
<string name="btn_decrypt_files">Изабери фајл</string>
<string name="btn_encrypt_files">Шифруј фајлове</string>
@@ -143,6 +145,7 @@
<string name="label_encryption_algorithm">Алгоритам шифровања</string>
<string name="label_hash_algorithm">Алгоритам хеша</string>
<string name="label_symmetric">Шифровање са лозинком</string>
+ <string name="label_passphrase_cache_ttl">Прилагодите изборе „памћења“</string>
<string name="label_passphrase_cache_subs">Памти лозинке по поткључу</string>
<string name="label_message_compression">Компресија текста</string>
<string name="label_file_compression">Компресија фајла</string>
@@ -251,21 +254,14 @@
<string name="choice_8hours">8 сати</string>
<string name="choice_forever">заувек</string>
<string name="choice_select_cert">Изаберите кључ</string>
- <string name="dsa">ДСА</string>
- <string name="elgamal">Елгамал</string>
- <string name="rsa">РСА</string>
- <string name="ecdh">ЕЦДХ</string>
- <string name="ecdsa">ЕЦДСА</string>
<string name="filemanager_title_open">Отвори…</string>
+ <string name="rsa_2048">РСА 2048</string>
+ <string name="rsa_3072">РСА 3072</string>
+ <string name="rsa_4096">РСА 4096</string>
<string name="error">Грешка</string>
<string name="error_message">Грешка: %s</string>
<string name="theme_dark">Тамна</string>
<string name="theme_light">Светла</string>
- <!--key flags-->
- <string name="flag_certify">Овера</string>
- <string name="flag_sign">Потпис</string>
- <string name="flag_encrypt">Шифровање</string>
- <string name="flag_authenticate">Аутентификовање</string>
<!--sentences-->
<string name="wrong_passphrase">Погрешна лозинка.</string>
<string name="no_filemanager_installed">Нема подесног менаџера фајлова.</string>
@@ -683,7 +679,6 @@
<item>Премести поткључ у безбедносни токен</item>
</string-array>
<string name="edit_key_new_subkey">нови поткључ</string>
- <string name="edit_key_select_flag">Изаберите бар једну заставицу!</string>
<string name="edit_key_error_add_identity">Додајте бар један идентитет!</string>
<string name="edit_key_error_add_subkey">Додајте бар један поткључ!</string>
<!--Create key-->
@@ -1271,6 +1266,7 @@
<string name="msg_lv_fp_error">Отисак се не поклапа!</string>
<string name="msg_lv_error_twitter_auth">Грешка добављања аутентификацијског токена за Твитер!</string>
<string name="msg_lv_error_twitter_response">Неочекивани одзив Твитер АПИ-ја!</string>
+ <string name="msg_lv_error_github_not_found">Гист не садржи поклапајуће фајлове!</string>
<string name="msg_lv_fetch">Добављам УРИ „%s“</string>
<string name="msg_lv_fetch_redir">Пратим преусмерење на „%s“</string>
<string name="msg_lv_fetch_ok">Успешно добављено (ХТТП %s)</string>
@@ -1427,7 +1423,6 @@
<string name="snack_security_token_import">Увези</string>
<string name="button_bind_key">Повежи кључ</string>
<string name="security_token_serial_no">Серијски број: %s</string>
- <string name="security_token_key_holder">Држач кључа: </string>
<string name="security_token_key_holder_not_set"><![CDATA[Држач кључа: <није постављено>]]></string>
<string name="btn_import">Увези</string>
<string name="btn_reset">Ресетуј</string>
@@ -1536,6 +1531,8 @@
<string name="linked_error_network">Грешка мреже!</string>
<string name="linked_error_http">Грешка комуникације: %s</string>
<string name="linked_webview_title_github">Гитхаб овлашћење</string>
+ <string name="linked_gist_description">Повезани идентитет Отвореног кључарника</string>
+ <string name="linked_empty">Повежите ваш кључ са Гитхабом, Твитером или осталим сајтовима!</string>
<string name="snack_btn_overwrite">Пребриши</string>
<string name="backup_code_explanation">Резерва ће бити обезбеђена кôдом за резерву. Запишите га пре него што наставите даље!</string>
<string name="backup_code_enter">Унесите кôд за резерву:</string>
@@ -1583,6 +1580,7 @@
<string name="help_donation_paypal_item">Донација Отвореном кључарнику</string>
<string-array name="help_donation_google_catalog_values">
<item>1 евро</item>
+ <item>2 евра</item>
<item>3 евра</item>
<item>5 евра</item>
<item>10 евра</item>
diff --git a/OpenKeychain/src/main/res/values-sv/strings.xml b/OpenKeychain/src/main/res/values-sv/strings.xml
index c5103d879..e7e24031d 100644
--- a/OpenKeychain/src/main/res/values-sv/strings.xml
+++ b/OpenKeychain/src/main/res/values-sv/strings.xml
@@ -30,6 +30,7 @@
<string name="title_help">Hjälp</string>
<string name="title_log_display">Logg</string>
<string name="title_exchange_keys">Utbyt nycklar</string>
+ <string name="title_advanced_key_info">Avancerad</string>
<string name="title_delete_secret_key">Radera DIN nyckel \'%s\'?</string>
<string name="title_manage_my_keys">Hantera mina nycklar</string>
<!--section-->
@@ -41,13 +42,16 @@
<string name="section_keys">Undernycklar</string>
<string name="section_cloud_search">Nyckelsökning</string>
<string name="section_cloud_search_summary">Nyckelserver, keybase.io</string>
+ <string name="section_passphrase_cache">Lösenord och pin-koder</string>
<string name="section_proxy_settings_summary">Tor, proxyinställningar</string>
<string name="section_gui">Gränssnitt</string>
+ <string name="section_experimental_features">Experimentella funktioner</string>
<string name="section_certify">Bekräfta</string>
<string name="section_actions">Åtgärder</string>
<string name="section_share_key">Nyckel</string>
<string name="section_key_server">Nyckelserver</string>
<string name="section_fingerprint">Fingeravtryck</string>
+ <string name="section_phrases">Uttryck</string>
<string name="section_encrypt">Kryptera</string>
<string name="section_decrypt">Dekryptera / Verifiera</string>
<string name="section_current_expiry">Aktuellt utgångsdatum</string>
@@ -68,12 +72,18 @@
<string name="btn_back">Föregående</string>
<string name="btn_no">Nej</string>
<string name="btn_match">Fingeravtrycken matchar</string>
+ <string name="btn_match_phrases">Uttryck matchar</string>
<string name="btn_share_encrypted_signed">Kryptera/signera och dela text</string>
<string name="btn_copy_encrypted_signed">Kryptera/signera och kopiera text</string>
+ <string name="btn_paste_encrypted_signed">Kryptera/signera och klistra in text</string>
<string name="btn_view_cert_key">Visa nyckel för certifiering</string>
<string name="btn_create_key">Skapa nyckel</string>
<string name="btn_add_files">Lägg till fil(er)</string>
+ <string name="btn_share_decrypted_text">Dela</string>
+ <string name="btn_open_with">Öppna med...</string>
+ <string name="btn_copy_decrypted_text">Kopiera till urklipp</string>
<string name="btn_decrypt_clipboard">Läs från urklipp</string>
+ <string name="btn_decrypt_files">Välj inmatningsfil</string>
<string name="btn_encrypt_files">Kryptera filer</string>
<string name="btn_encrypt_text">Kryptera text</string>
<string name="btn_add_email">Lägg till extra e-postadress</string>
@@ -81,7 +91,10 @@
<string name="btn_add_keyserver">Lägg till</string>
<string name="btn_save_default">Spara som standard</string>
<string name="btn_saved">Sparad!</string>
+ <string name="btn_not_matching">Inga matchningar</string>
<!--Content Description-->
+ <string name="cd_encrypt_files">Kryptera filer</string>
+ <string name="cd_encrypt_text">Kryptera text</string>
<!--menu-->
<string name="menu_preferences">Inställningar</string>
<string name="menu_help">Hjälp</string>
@@ -94,6 +107,7 @@
<string name="menu_select_all">Markera alla</string>
<string name="menu_export_all_keys">Exportera alla nycklar</string>
<string name="menu_update_all_keys">Uppdatera alla nycklar</string>
+ <string name="menu_advanced">Avancerad</string>
<string name="menu_keyserver_add">Lägg till</string>
<!--label-->
<string name="label_message">Text</string>
@@ -184,29 +198,24 @@
<string name="choice_4hours">4 timmar</string>
<string name="choice_8hours">8 timmar</string>
<string name="choice_forever">för alltid</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
+ <string name="choice_select_cert">Välj en nyckel</string>
<string name="filemanager_title_open">Öppna…</string>
<string name="error">Fel</string>
<string name="error_message">Fel: %s</string>
- <!--key flags-->
- <string name="flag_certify">Certifiera</string>
- <string name="flag_sign">Signera</string>
- <string name="flag_encrypt">Kryptera</string>
- <string name="flag_authenticate">Autentisera</string>
+ <string name="theme_dark">Mörk</string>
+ <string name="theme_light">Ljus</string>
<!--sentences-->
<string name="wrong_passphrase">Fel lösenord.</string>
<string name="no_filemanager_installed">Ingen kompatibel filhanterare är installerad.</string>
<string name="passphrases_do_not_match">Lösenorden stämde inte överens.</string>
<string name="passphrase_must_not_be_empty">Var god ange ett lösenord.</string>
+ <string name="passphrase_for_symmetric_encryption">Skriv lösenord</string>
<string name="passphrase_for">Ange lösenord för \'%s\'</string>
<string name="pin_for">Ange PIN för \'%s\'</string>
<string name="file_delete_confirmation_title">Radera orginalfiler?</string>
<string name="file_delete_confirmation">Följande filer kommer raderas:%s</string>
<string name="file_delete_successful">%1$d av %2$d filer har raderats.%3$s</string>
+ <string name="no_file_selected">Inga valda filer.</string>
<string name="encrypt_sign_successful">Signerades och/eller krypterades.</string>
<string name="encrypt_sign_clipboard_successful">Signerades och/eller krypterades till urklipp.</string>
<string name="select_encryption_key">Välj åtminstone en krypteringsnyckel.</string>
@@ -353,6 +362,7 @@
<string name="help_tab_changelog">Ändringslogg</string>
<string name="help_tab_about">Om</string>
<string name="help_about_version">Version:</string>
+ <string name="help_tab_donations">Donera</string>
<!--Import-->
<string name="import_tab_keyserver">Nyckelserver</string>
<string name="import_tab_cloud">Nyckelsökning</string>
@@ -432,6 +442,7 @@
<string name="api_settings_delete_account">Radera konto</string>
<string name="api_settings_package_name">Paketnamn</string>
<string name="api_settings_accounts">Konton (gamla API:t)</string>
+ <string name="api_settings_advanced">Avancerad</string>
<string name="api_settings_allowed_keys">Tillåtna nycklar</string>
<string name="api_settings_settings">Inställningar</string>
<string name="api_settings_key">Kontonyckel:</string>
@@ -514,7 +525,6 @@
<string name="edit_key_edit_user_id_revoked">Den här identiteten har återkallats. Detta kan inte göras ogjort.</string>
<string name="edit_key_edit_subkey_title">Välj en åtgärd!</string>
<string name="edit_key_new_subkey">ny undernyckel</string>
- <string name="edit_key_select_flag">Välj åtminstone en flagga!</string>
<string name="edit_key_error_add_identity">Lägg till åtminstone en identitet!</string>
<string name="edit_key_error_add_subkey">Lägg till åtminstone en undernyckel!</string>
<!--Create key-->
@@ -532,6 +542,7 @@
<string name="create_key_add_email">Lägg till e-postadress</string>
<string name="create_key_email_already_exists_text">E-postadress har redan lagts till</string>
<string name="create_key_email_invalid_email">Formatet på e-postadressen är ogiltigt</string>
+ <string name="create_key_yubi_key_pin">PIN</string>
<!--View key-->
<string name="view_key_revoked">Återkallad: Nyckeln bör inte användas längre!</string>
<string name="view_key_expired">Utgånget: Kontakten behöver utöka nyckelns giltighetstid!</string>
@@ -780,6 +791,10 @@
<string name="msg_dc_unlocking">Låser upp privat nyckel</string>
<!--Messages for VerifySignedLiteralData operation-->
<string name="msg_vl_clear_meta_file">Filnamn: %s</string>
+ <string name="msg_vl_clear_meta_mime">MIME-typ: %s</string>
+ <string name="msg_vl_clear_meta_size">Filstorlek: %s</string>
+ <string name="msg_vl_error_integrity_check">Fel vid integritetskontroll!</string>
+ <string name="msg_vl_ok">Ok</string>
<!--Messages for SignEncrypt operation-->
<string name="msg_se_success">Signering/kryptering lyckades!</string>
<!--Messages for PgpSignEncrypt operation-->
@@ -863,6 +878,7 @@
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <string name="enter_passphrase">Skriv lösenord</string>
<string name="passphrase_again">Igen</string>
<string name="lockpattern">Låsmönster</string>
<string name="lockpatternNFC">NFC + Låsmönster</string>
@@ -883,4 +899,18 @@
<string name="linked_title_twitter">Twitter</string>
<string name="linked_button_verify">Verifiera</string>
<string name="linked_text_confirming">Bekräftar…</string>
+ <string name="share_log_dialog_share_button">Dela</string>
+ <string name="prompt_fidesmo_pgp_install_button_positive">Installera</string>
+ <string name="prompt_fidesmo_pgp_install_button_negative">Avbryt</string>
+ <string name="prompt_fidesmo_app_install_button_positive">Installera</string>
+ <string name="prompt_fidesmo_app_install_button_negative">Avbryt</string>
+ <string-array name="help_donation_google_catalog_values">
+ <item>1 EUR</item>
+ <item>2 EUR</item>
+ <item>3 EUR</item>
+ <item>5 EUR</item>
+ <item>10 EUR</item>
+ <item>50 EUR</item>
+ <item>100 EUR</item>
+ </string-array>
</resources>
diff --git a/OpenKeychain/src/main/res/values-sw360dp/styles.xml b/OpenKeychain/src/main/res/values-sw360dp/styles.xml
new file mode 100644
index 000000000..70587b7dc
--- /dev/null
+++ b/OpenKeychain/src/main/res/values-sw360dp/styles.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:tools="http://schemas.android.com/tools">
+
+ <!-- constrain size of backup code input for smaller devices -->
+ <style name="BackupCodeTextStyle">
+ <item name="android:textSize" tools:ignore="SpUsage">16dp</item>
+ </style>
+
+</resources>
diff --git a/OpenKeychain/src/main/res/values-sw400dp/styles.xml b/OpenKeychain/src/main/res/values-sw400dp/styles.xml
new file mode 100644
index 000000000..b18c708d9
--- /dev/null
+++ b/OpenKeychain/src/main/res/values-sw400dp/styles.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:tools="http://schemas.android.com/tools">
+
+ <!-- constrain size of backup code input for smaller devices -->
+ <style name="BackupCodeTextStyle">
+ <item name="android:textSize" tools:ignore="SpUsage">18dp</item>
+ </style>
+
+</resources>
diff --git a/OpenKeychain/src/main/res/values-tr/strings.xml b/OpenKeychain/src/main/res/values-tr/strings.xml
index 32b24712f..618f26bbf 100644
--- a/OpenKeychain/src/main/res/values-tr/strings.xml
+++ b/OpenKeychain/src/main/res/values-tr/strings.xml
@@ -106,19 +106,9 @@
<string name="choice_4hours">4 saat</string>
<string name="choice_8hours">8 saat</string>
<string name="choice_forever">daima</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Aç...</string>
<string name="error">Hata</string>
<string name="error_message">Hata: %s</string>
- <!--key flags-->
- <string name="flag_certify">Tasdikleme</string>
- <string name="flag_sign">İmzalama</string>
- <string name="flag_encrypt">Şifreleme</string>
- <string name="flag_authenticate">Kimlik kanıtlama</string>
<!--sentences-->
<string name="no_filemanager_installed">Uyumlu dosya yöneticisi yüklenmedi.</string>
<string name="encrypt_sign_successful">Başarıyla imzalandı ve/veya şifrelendi.</string>
@@ -326,7 +316,6 @@
</string-array>
<string name="edit_key_edit_subkey_title">Bir eylem seç!</string>
<string name="edit_key_new_subkey">yeni alt anahtar</string>
- <string name="edit_key_select_flag">Lütfen en az bir bayrak seçiniz!</string>
<string name="edit_key_error_add_identity">En az bir kimlik ekleyin!</string>
<string name="edit_key_error_add_subkey">En az bir alt anahtar ekleyin!</string>
<!--Create key-->
diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml
index df0667f31..93bf6ee68 100644
--- a/OpenKeychain/src/main/res/values-uk/strings.xml
+++ b/OpenKeychain/src/main/res/values-uk/strings.xml
@@ -108,19 +108,9 @@
<string name="choice_4hours">4 години</string>
<string name="choice_8hours">8 годин</string>
<string name="choice_forever">назавжди</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Відкрити…</string>
<string name="error">Помилка</string>
<string name="error_message">Помилка: %s</string>
- <!--key flags-->
- <string name="flag_certify">Сертифікувати</string>
- <string name="flag_sign">Підписати</string>
- <string name="flag_encrypt">Зашифрувати</string>
- <string name="flag_authenticate">Перевірити справжність</string>
<!--sentences-->
<string name="no_filemanager_installed">Нема встановленого сумісного менеджера файлів.</string>
<string name="encrypt_sign_successful">Успішно підписано та/або перевірено.</string>
@@ -330,7 +320,6 @@
<string name="edit_key_edit_user_id_revoked">Ця сутність вже відкликана. Це не можна скасувати.</string>
<string name="edit_key_edit_subkey_title">Виберіть дію!</string>
<string name="edit_key_new_subkey">новий підключ</string>
- <string name="edit_key_select_flag">Будь ласка, виберіть хоча б один прапор!</string>
<string name="edit_key_error_add_identity">Додати хоча б одну сутність!</string>
<string name="edit_key_error_add_subkey">Додати хоча б один підключ!</string>
<!--Create key-->
diff --git a/OpenKeychain/src/main/res/values-vi/strings.xml b/OpenKeychain/src/main/res/values-vi/strings.xml
index b0e2fc1b8..86053fba1 100644
--- a/OpenKeychain/src/main/res/values-vi/strings.xml
+++ b/OpenKeychain/src/main/res/values-vi/strings.xml
@@ -15,7 +15,6 @@
<!--InstallDialogFragment strings-->
<!--StartOrbotDialogFragment strings-->
<!--choice-->
- <!--key flags-->
<!--sentences-->
<!--errors
no punctuation, all lowercase,
diff --git a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml
index 08b63d13b..0a6e0c7b4 100644
--- a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml
+++ b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml
@@ -8,6 +8,7 @@
<string name="title_encrypt_files">加密</string>
<string name="title_decrypt">解密</string>
<string name="title_add_subkey">新增子金鑰</string>
+ <string name="title_change_master_key">修改主密匙</string>
<string name="title_edit_key">編輯金鑰</string>
<string name="title_preferences">設定</string>
<string name="title_api_registered_apps">應用程式</string>
@@ -181,21 +182,11 @@
<string name="choice_4hours">4小時</string>
<string name="choice_8hours">8小時</string>
<string name="choice_forever">永久</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">開啟…</string>
<string name="error">錯誤</string>
<string name="error_message">錯誤: %s</string>
<string name="theme_dark">深色</string>
<string name="theme_light">亮色</string>
- <!--key flags-->
- <string name="flag_certify">簽署</string>
- <string name="flag_sign">簽名</string>
- <string name="flag_encrypt">加密</string>
- <string name="flag_authenticate">驗證</string>
<!--sentences-->
<string name="wrong_passphrase">密碼錯誤。</string>
<string name="no_filemanager_installed">找不到相容的檔案管理員。</string>
@@ -513,7 +504,6 @@
<string name="edit_key_edit_user_id_revoked">這個身分識別已被撤銷。此動作無法還原。</string>
<string name="edit_key_edit_subkey_title">選擇一個動作!</string>
<string name="edit_key_new_subkey">新增子金鑰</string>
- <string name="edit_key_select_flag">請至少選擇一個用途!</string>
<string name="edit_key_error_add_identity">新增至少一組身分識別!</string>
<string name="edit_key_error_add_subkey">新增至少一組子金鑰!</string>
<!--Create key-->
diff --git a/OpenKeychain/src/main/res/values-zh/strings.xml b/OpenKeychain/src/main/res/values-zh/strings.xml
index 301057794..b29ac857b 100644
--- a/OpenKeychain/src/main/res/values-zh/strings.xml
+++ b/OpenKeychain/src/main/res/values-zh/strings.xml
@@ -37,7 +37,7 @@
<string name="title_manage_my_keys">管理我的密钥</string>
<!--section-->
<string name="section_user_ids">用户名</string>
- <string name="section_security_token">安全凭证</string>
+ <string name="section_security_token">安全令牌</string>
<string name="section_linked_system_contact">关联系统联系人</string>
<string name="section_keybase_proofs">Keybase.io 证书</string>
<string name="section_should_you_trust">信任此密钥?</string>
@@ -187,6 +187,7 @@
<string name="label_sync_settings_keyserver_title">自动更新密钥</string>
<string name="label_sync_settings_keyserver_summary_on">每三天,自动从密钥服务器更新</string>
<string name="label_sync_settings_keyserver_summary_off">不自动更新密钥</string>
+ <string name="label_sync_settings_wifi_title">仅在Wi-Fi环境同步</string>
<string name="label_sync_settings_contacts_title">关联联系方式</string>
<string name="label_sync_settings_contacts_summary_on">根据名字和 email 地址将密钥关联到联系人。这是本地操作,不需要联网。</string>
<string name="label_sync_settings_contacts_summary_off">新密钥将不关联到联系方式</string>
@@ -252,21 +253,11 @@
<string name="choice_8hours">8小时</string>
<string name="choice_forever">永远</string>
<string name="choice_select_cert">选择一个密钥</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="ecdh">ECDH</string>
- <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">打开...</string>
<string name="error">错误</string>
<string name="error_message">错误:%s</string>
<string name="theme_dark">深色主题</string>
<string name="theme_light">亮色主题</string>
- <!--key flags-->
- <string name="flag_certify">验证</string>
- <string name="flag_sign">签名</string>
- <string name="flag_encrypt">加密</string>
- <string name="flag_authenticate">认证</string>
<!--sentences-->
<string name="wrong_passphrase">密码错误</string>
<string name="no_filemanager_installed">安装了不匹配的文件管理器</string>
@@ -406,15 +397,15 @@
<string name="progress_generating_signature">产生签名中...</string>
<string name="progress_verifying_signature">正在验证签名...</string>
<string name="progress_signing">正在签名...</string>
- <string name="progress_certifying">确认中...</string>
+ <string name="progress_certifying">正在确认...</string>
<string name="progress_reading_data">正在读取数据</string>
<string name="progress_finding_key">正在查找密钥</string>
- <string name="progress_decompressing_data">解压缩中...</string>
+ <string name="progress_decompressing_data">正在解压缩...</string>
<string name="progress_verifying_integrity">验证完整性...</string>
<string name="progress_deleting_securely">安全地删除 \'%s\'…</string>
- <string name="progress_deleting">删除密钥中...</string>
+ <string name="progress_deleting">正在删除密钥...</string>
<string name="progress_con_saving">合并:正在保存到缓存......</string>
- <string name="progress_con_reimport">合并:重新导入中......</string>
+ <string name="progress_con_reimport">合并:正在重新导入......</string>
<string name="progress_verifying_keyserver_connection">验证连接中...</string>
<string name="progress_starting_orbot">启动Orbot...</string>
<!--action strings-->
@@ -660,7 +651,6 @@
<item>把子密钥转移到安全令牌</item>
</string-array>
<string name="edit_key_new_subkey">新建子密钥</string>
- <string name="edit_key_select_flag">至少选择一个标志!</string>
<string name="edit_key_error_add_identity">至少要有一个用户标识!</string>
<string name="edit_key_error_add_subkey">至少要有一个子密钥!</string>
<string name="edit_key_error_bad_security_token_algo">安全令牌不支持该算法</string>
@@ -721,6 +711,7 @@
<!--certs-->
<string name="cert_default">默认</string>
<string name="cert_none">无</string>
+ <string name="cert_casual">随意</string>
<string name="cert_positive">正面的</string>
<string name="cert_revoke">已吊销</string>
<string name="cert_verify_ok">正常</string>
@@ -796,20 +787,30 @@
<string name="msg_ip_uid_cert_old">证书比前一个更老,跳过</string>
<string name="msg_ip_uid_cert_new">证书是最近的,代替前一个</string>
<string name="msg_ip_uid_cert_good">%1$s 发现良好的证书</string>
+ <string name="msg_ip_uid_reorder">正在重新排序用户ID</string>
+ <string name="msg_ip_uid_processing">正在处理用户ID %s</string>
+ <string name="msg_ip_uid_revoked">用户ID已吊销</string>
+ <string name="msg_ip_uat_processing_image">正在处理图像类型的用户属性</string>
+ <string name="msg_ip_uat_processing_unknown">正在处理未知类型的用户属性</string>
<string name="msg_ip_uat_cert_bad">发现损坏的证书!</string>
<string name="msg_ip_uat_cert_error">证书处理错误</string>
<string name="msg_ip_uat_cert_nonrevoke">已有一个未吊销的证书,跳过</string>
<string name="msg_ip_uat_cert_old">证书比前一个更老,跳过</string>
<string name="msg_ip_uat_cert_new">证书是最近的,代替前一个</string>
<string name="msg_ip_uat_cert_good">%1$s 发现良好的证书</string>
+ <string name="msg_ip_uat_classifying">对用户属性进行分类</string>
+ <string name="msg_ip_uat_revoked">用户属性已撤销</string>
<string name="msg_is_bad_type_public">试图把公钥导入为私钥,这是一个漏洞,请报告错误!</string>
<!--Import Secret log entries-->
+ <string name="msg_is">导入密钥 %s</string>
<string name="msg_is_db_exception">数据库错误!</string>
+ <string name="msg_is_error_io_exc">密钥环编码时出错!</string>
<string name="msg_is_merge_public">正在合并导入的数据到现有的公钥钥匙环</string>
<string name="msg_is_merge_secret">正在合并已导入的数据到现有的私有钥匙环</string>
<string name="msg_is_success_identical">钥匙环不包含任何新的数据,无动作</string>
<string name="msg_is_success">成功导入私密钥匙环</string>
<!--Keyring Canonicalization log entries-->
+ <string name="msg_kc_error_master_algo">该主密钥使用了一个未知算法(%s)!</string>
<string name="msg_kc_master">正在处理主密钥</string>
<string name="msg_kc_master_bad_type">正在移除未知类型 (%s) 的主密钥</string>
<string name="msg_kc_master_bad_local">正在移除标记为\'local\'的主密钥</string>
@@ -817,12 +818,20 @@
<string name="msg_kc_master_bad">正在移除损坏的主密钥证书</string>
<string name="msg_kc_master_local">正在移除标记为\'local\'的主密钥</string>
<string name="msg_kc_sub">正在处理子密钥 %s</string>
+ <string name="msg_kc_uat_unknown">正在处理未知类型的用户属性</string>
<!--Keyring merging log entries-->
+ <string name="msg_mg_unchanged">无物可合并</string>
<!--createSecretKeyRing-->
<!--modifySecretKeyRing-->
+ <string name="msg_mf_passphrase_key">用新密码重新加密子密钥%s</string>
+ <string name="msg_mf_subkey_new_id">新建子密钥ID:%s</string>
+ <string name="msg_mf_uat_add_unknown">正在添加未知类型的用户属性</string>
+ <string name="msg_mf_unlock_error">密钥环解锁时出错!</string>
+ <string name="msg_mf_unlock">正在解锁密钥环</string>
<!--Consolidate-->
<string name="msg_con_db_clear">正在清空数据库</string>
<string name="msg_con_success">成功整理数据库</string>
+ <string name="msg_con_error_db">打开数据库时出错!</string>
<string name="msg_con_error_public">重新导入公共密钥时出错</string>
<string name="msg_con_error_secret">重新导入私有密钥时出错</string>
<!--Edit Key (higher level than modify)-->
@@ -832,19 +841,27 @@
<!--Other messages used in OperationLogs-->
<string name="msg_ek_error_not_found">未找到密钥</string>
<!--Messages for DecryptVerify operation-->
+ <string name="msg_dc_clear_meta_size">文件大小:%s</string>
+ <string name="msg_dc_clear_meta_size_unknown">文件大小未知</string>
<string name="msg_dc">正在开始解密操作...</string>
<string name="msg_dc_unlocking">正在解密私钥</string>
<!--Messages for VerifySignedLiteralData operation-->
+ <string name="msg_vl_clear_meta_size">文件大小:%s</string>
<string name="msg_vl_ok">正常</string>
<!--Messages for SignEncrypt operation-->
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_crt_error_nothing">没有密钥被签名</string>
<string name="msg_crt">正在为密钥环签名...</string>
<string name="msg_crt_saving">保存密钥环中....</string>
+ <string name="msg_crt_unlock">正在解锁主密钥</string>
<string name="msg_crt_warn_not_found">未找到密钥</string>
<string name="msg_backup_error_db">数据库错误!</string>
<string name="msg_upload_error_not_found">未找到密钥</string>
+ <string name="msg_del_error_empty">无物可删!</string>
+ <string name="msg_del_error_multi_secret">密钥只能逐个删除!</string>
+ <string name="msg_revoke_error_empty">无物可撤销!</string>
<!--Linked Identity verification-->
+ <string name="msg_data">正在输入数据</string>
<!--Messages for Keybase Verification operation-->
<string name="msg_keybase_error_key_mismatch">证明内容与该密钥指纹不匹配</string>
<string name="msg_keybase_error_dns_fail">取回 DNS 的 TXT 记录失败</string>
@@ -863,9 +880,12 @@
<!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
+ <string name="title_unlock_method">选择一个解锁方法</string>
<!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<string name="enter_passphrase">输入密码</string>
+ <string name="unlock_method">解锁方法</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
+ <string name="unlocked">已解锁</string>
<string name="nfc_settings">设置</string>
<string name="snack_compression_on">压缩 <b>启用</b>.</string>
<string name="snack_compression_off">压缩 <b>禁用</b>.</string>
diff --git a/OpenKeychain/src/main/res/values/colors.xml b/OpenKeychain/src/main/res/values/colors.xml
index 93cf126f7..d9075b587 100644
--- a/OpenKeychain/src/main/res/values/colors.xml
+++ b/OpenKeychain/src/main/res/values/colors.xml
@@ -35,5 +35,6 @@
<!-- linked ID view -->
<color name="card_view_button">#7bad45</color>
+ <color name="toolbar_photo_tint">#1E7bad45</color>
</resources>
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 725cd7b41..387522fc1 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -13,6 +13,7 @@
<string name="title_encrypt_files">"Encrypt"</string>
<string name="title_decrypt">"Decrypt"</string>
<string name="title_add_subkey">"Add subkey"</string>
+ <string name="title_change_master_key">Change master key</string>
<string name="title_edit_key">"Edit Key"</string>
<string name="title_linked_create">"Create a Linked Identity"</string>
<string name="title_preferences">"Settings"</string>
@@ -40,6 +41,7 @@
<string name="title_advanced_key_info">"Advanced"</string>
<string name="title_delete_secret_key">"Delete YOUR key '%s'?"</string>
<string name="title_manage_my_keys">"Manage my keys"</string>
+ <string name="title_alert_strip">"Strip this subkey"</string>
<!-- section -->
<string name="section_user_ids">"Identities"</string>
@@ -165,6 +167,7 @@
<string name="label_keyservers">"Select OpenPGP keyservers"</string>
<string name="label_key_id">"Key ID"</string>
<string name="label_key_created">"Key created %s"</string>
+ <string name="label_key_type">"Type"</string>
<string name="label_creation">"Creation"</string>
<string name="label_expiry">"Expiry"</string>
<string name="label_usage">"Usage"</string>
@@ -201,6 +204,7 @@
<string name="label_sync_settings_keyserver_title">"Automatic key updates"</string>
<string name="label_sync_settings_keyserver_summary_on">"Every three days, keys are updated from the preferred keyserver"</string>
<string name="label_sync_settings_keyserver_summary_off">"Keys are not automatically updated"</string>
+ <string name="label_sync_settings_wifi_title">"Sync only on Wi-Fi"</string>
<string name="label_sync_settings_contacts_title">"Link keys to contacts"</string>
<string name="label_sync_settings_contacts_summary_on">"Link keys to contacts based on names and email addresses. This happens completely offline on your device."</string>
<string name="label_sync_settings_contacts_summary_off">"New keys will not be linked to contacts"</string>
@@ -281,22 +285,26 @@
<string name="choice_8hours">"8 hours"</string>
<string name="choice_forever">"forever"</string>
<string name="choice_select_cert">"Select a Key"</string>
- <string name="dsa">"DSA"</string>
- <string name="elgamal">"ElGamal"</string>
- <string name="rsa">"RSA"</string>
- <string name="ecdh">"ECDH"</string>
- <string name="ecdsa">"ECDSA"</string>
<string name="filemanager_title_open">"Open…"</string>
+ <string name="rsa_2048">"RSA 2048"</string>
+ <string name="rsa_2048_description_html">"smaller file size, considered secure until 2030"</string>
+ <string name="rsa_3072">"RSA 3072"</string>
+ <string name="rsa_3072_description_html">"recommended, considered secure until 2040"</string>
+ <string name="rsa_4096">"RSA 4096"</string>
+ <string name="rsa_4096_description_html">"larger file size, considered secure until 2040+"</string>
+ <string name="ecc_p256">"ECC P-256"</string>
+ <string name="ecc_p256_description_html">"very tiny file size, considered secure until 2040 &lt;br/> &lt;u>experimental and not supported by all implementations&lt;/u>"</string>
+ <string name="ecc_p521">"ECC P-521"</string>
+ <string name="ecc_p521_description_html">"tiny file size, considered secure until 2040+ &lt;br/> &lt;u>experimental and not supported by all implementations&lt;/u>"</string>
+ <string name="usage_none">"None (subkey binding only)"</string>
+ <string name="usage_sign">"Sign"</string>
+ <string name="usage_encrypt">"Encrypt"</string>
+ <string name="usage_sign_and_encrypt">"Sign &amp; Encrypt"</string>
<string name="error">"Error"</string>
<string name="error_message">"Error: %s"</string>
<string name="theme_dark">"Dark"</string>
<string name="theme_light">"Light"</string>
-
- <!-- key flags -->
- <string name="flag_certify">"Certify"</string>
- <string name="flag_sign">"Sign"</string>
- <string name="flag_encrypt">"Encrypt"</string>
- <string name="flag_authenticate">"Authenticate"</string>
+ <string name="strip">"Strip it"</string>
<!-- sentences -->
<string name="wrong_passphrase">"Wrong password."</string>
@@ -331,6 +339,7 @@
<string name="public_key_deletetion_confirmation">"Delete key '%s'?"</string>
<string name="also_export_secret_keys">"Also export secret keys"</string>
<string name="reinstall_openkeychain">"You encountered a known bug with Android. Please reinstall OpenKeychain if you want to link your contacts with keys."</string>
+ <string name="alert_strip">"Stripping this subkey will make it unusable on this device!"</string>
<string name="key_exported">"Successfully exported 1 key."</string>
<string name="keys_exported">"Successfully exported %d keys."</string>
@@ -358,6 +367,7 @@
<string name="error_file_delete_failed">"have not been deleted. Delete them manually!"</string>
<string name="error_file_added_already">%s has already been added.</string>
<string name="error_file_not_found">"file not found"</string>
+ <string name="error_bad_data">"Bad data!"</string>
<string name="error_no_secret_key_found">"no suitable secret key found"</string>
<string name="error_external_storage_not_ready">"external storage not ready"</string>
<string name="error_key_size_minimum512bit">"key size must be at least 512bit"</string>
@@ -370,6 +380,7 @@
<string name="error_integrity_check_failed">"integrity check failed! Data has been modified!"</string>
<string name="error_wrong_passphrase">"wrong password"</string>
<string name="error_could_not_extract_private_key">"could not extract private key"</string>
+ <string name="error_wrong_security_token">"this security token doesn't contain required key"</string>
<!-- errors without preceeding Error: -->
<string name="error_jelly_bean_needed">"You need Android 4.1 to use Android's NFC Beam feature!"</string>
@@ -566,7 +577,7 @@
<item quantity="other">"Successfully deleted %1$d keys%2$s."</item>
</plurals>
<plurals name="delete_fail">
- <item quantity="one">"Error deleting one key%2$s."</item>
+ <item quantity="one">"Error deleting one key."</item>
<item quantity="other">"Error deleting %1$d keys."</item>
</plurals>
<string name="delete_nothing">"Nothing to delete."</string>
@@ -746,12 +757,12 @@
<item>"Move Subkey to Security Token"</item>
</string-array>
<string name="edit_key_new_subkey">"new subkey"</string>
- <string name="edit_key_select_flag">"Please select at least one flag!"</string>
+ <string name="edit_key_select_usage">"Please select key usage!"</string>
<string name="edit_key_error_add_identity">"Add at least one identity!"</string>
<string name="edit_key_error_add_subkey">"Add at least one subkey!"</string>
<string name="edit_key_error_bad_security_token_algo">"Algorithm not supported by Security Token!"</string>
<string name="edit_key_error_bad_security_token_size">"Key size not supported by Security Token!"</string>
- <string name="edit_key_error_bad_security_token_stripped">"Cannot move key to Security Token (either stripped or 'divert-to-card')!"</string>
+ <string name="edit_key_error_bad_security_token_stripped">"Cannot move key to Security Token (either stripped or already on Security Token)!"</string>
<!-- Create key -->
<string name="create_key_upload">"Synchronize with the Internet"</string>
@@ -759,6 +770,7 @@
<string name="create_key_passphrases_not_equal">"Passwords do not match"</string>
<string name="create_key_final_text">"You entered the following identity:"</string>
<string name="create_key_final_robot_text">"Creating a key may take a while, have a cup of coffee in the meantime…"</string>
+ <string name="create_key_final_email_valid_warning">"Many applications work better when provided with keys only containing valid email addresses"</string>
<string name="create_key_rsa">"(3 subkeys, RSA, 4096 bit)"</string>
<string name="create_key_custom">"(custom key configuration)"</string>
<string name="create_key_name_text">"Choose a name associated with this key. This can be a full name, e.g., 'John Doe', or a nickname, e.g., 'Johnny'."</string>
@@ -769,7 +781,7 @@
<string name="create_key_add_email">"Add email address"</string>
<string name="create_key_add_email_text">"Additional email addresses are also associated to this key and can be used for secure communication."</string>
<string name="create_key_email_already_exists_text">"Email address has already been added"</string>
- <string name="create_key_email_invalid_email">"Email address format is invalid"</string>
+ <string name="create_key_email_empty_email">"Email address can not be empty"</string>
<string name="create_key_yubi_key_pin_text">"Please choose a PIN with 6 numbers."</string>
<string name="create_key_yubi_key_admin_pin_text">"Please write down the Admin PIN and store it in a safe place (required when you used a wrong PIN 3 times)."</string>
<string name="create_key_yubi_key_pin">"PIN"</string>
@@ -941,7 +953,7 @@
<string name="msg_is_subkey_empty">"Marked secret subkey %s as available, with empty password"</string>
<string name="msg_is_subkey_pin">"Marked secret subkey %s as available, with PIN"</string>
<string name="msg_is_subkey_stripped">"Marked secret subkey %s as stripped"</string>
- <string name="msg_is_subkey_divert">"Marked secret subkey %s as 'divert-to-card'"</string>
+ <string name="msg_is_subkey_divert">"Marked secret subkey %s as key on Security Token"</string>
<string name="msg_is_success_identical">"Keyring contains no new data, nothing to do"</string>
<string name="msg_is_success">"Successfully imported secret keyring"</string>
@@ -1050,9 +1062,9 @@
<!-- modifySecretKeyRing -->
<string name="msg_mr">"Modifying keyring %s"</string>
- <string name="msg_mf_divert">"Will divert to Security Token for crypto operations"</string>
- <string name="msg_mf_error_divert_newsub">"Creation of new subkeys is not supported for 'divert-to-card' primary keys!"</string>
- <string name="msg_mf_error_divert_serial">"The serial number of a 'divert-to-card' key must be 16 bytes! This is a programming error, please file a bug report!"</string>
+ <string name="msg_mf_divert">"Will use Security Token for crypto operations"</string>
+ <string name="msg_mf_error_divert_newsub">"Creation of new subkeys is not supported for primary keys on Security Tokens!"</string>
+ <string name="msg_mf_error_divert_serial">"The serial number of a key on Security Tokens must be 16 bytes! This is a programming error, please file a bug report!"</string>
<string name="msg_mf_error_encode">"Encoding exception!"</string>
<string name="msg_mf_error_fingerprint">"Actual key fingerprint does not match the expected one!"</string>
<string name="msg_mf_error_keyid">"No key ID. This is an internal error, please file a bug report!"</string>
@@ -1085,7 +1097,7 @@
<string name="msg_mf_primary_new">"Generating new certificate for new primary user ID"</string>
<string name="msg_mf_restricted_mode">"Changing to restricted operation mode"</string>
<string name="msg_mf_subkey_change">"Modifying subkey %s"</string>
- <string name="msg_mf_require_divert">"Diverting to Security Token for crypto operations"</string>
+ <string name="msg_mf_require_divert">"Using Security Token for crypto operations"</string>
<string name="msg_mf_require_passphrase">"Password required for operations"</string>
<string name="msg_mf_subkey_new">"Adding new subkey of type %s"</string>
<string name="msg_mf_subkey_new_id">"New subkey ID: %s"</string>
@@ -1181,7 +1193,7 @@
<string name="msg_dc_clear_signature">"Saving signature data for later"</string>
<string name="msg_dc_clear">"Processing cleartext data"</string>
<string name="msg_dc_error_bad_passphrase">"Error unlocking key, bad password!"</string>
- <string name="msg_dc_error_sym_passphrase">"Error decrypting data! (Bad passphrase?)"</string>
+ <string name="msg_dc_error_sym_passphrase">"Error decrypting data! (Bad password?)"</string>
<string name="msg_dc_error_corrupt_data">"Data is corrupt!"</string>
<string name="msg_dc_error_extract_key">"Unknown error unlocking key!"</string>
<string name="msg_dc_error_integrity_check">"Integrity check error!"</string>
@@ -1486,7 +1498,7 @@
<string name="error_key_not_found">"Key not found!"</string>
<string name="error_key_processing">"Error processing key!"</string>
<string name="key_stripped">"stripped"</string>
- <string name="key_divert">"divert to Security Token"</string>
+ <string name="key_divert">"on Security Token"</string>
<string name="key_no_passphrase">"no password"</string>
<string name="key_unavailable">"unavailable"</string>
<string name="secret_cannot_multiple">"Your own keys can only be deleted individually!"</string>
@@ -1544,7 +1556,7 @@
<string name="snack_security_token_import">"Import"</string>
<string name="button_bind_key">"Bind Key"</string>
<string name="security_token_serial_no">"Serial No: %s"</string>
- <string name="security_token_key_holder">"Key holder: "</string>
+ <string name="security_token_key_holder">"Key holder: %s"</string>
<string name="security_token_key_holder_not_set"><![CDATA[Key holder: <not set>]]></string>
<string name="security_token_status_bound">"Security Token matches and is bound to key"</string>
<string name="security_token_status_unbound">"Security Token matches, can be bound to key"</string>
@@ -1624,7 +1636,7 @@
<string name="linked_create_twitter_handle">Twitter Handle</string>
<string name="linked_create_twitter_2_1">"Touch either button to tweet the message!"</string>
<string name="linked_create_twitter_2_2">"You can edit the Tweet before posting it, so long as the text inside the brackets is unmodified."</string>
- <string name="linked_create_twitter_2_3">"Once your Tweet is published as &lt;b&gt;@%s&lt;/b&gt;, touch the Verify button to scan your timeline for it."</string>
+ <string name="linked_create_twitter_2_3">"Once your Tweet is published as <b>@%s</b>, touch the Verify button to scan your timeline for it."</string>
<string name="linked_create_twitter_2_4">"After successful verification, touch Finish button to add the Linked Identity to your keyring and finish the process."</string>
<string name="linked_create_verify">"Verify"</string>
@@ -1647,7 +1659,7 @@
<string name="linked_select_1">"A 'linked identity' connects your PGP key to a resource on the web."</string>
<string name="linked_select_2">"Please select a type:"</string>
<string name="linked_id_generic_text">"This file claims ownership of the OpenPGP key with long id %2$s.\n\nToken for proof:\n%1$s"</string>
- <string name="linked_id_github_text">"This Gist confirms the Linked Identity in my OpenPGP key, and links it to this GitHub account.\n\nToken for proof:\n%1$s"</string>
+ <string name="linked_id_github_text" translatable="false">"This Gist confirms the Linked Identity in my OpenPGP key, and links it to this GitHub account.\n\nToken for proof:\n%1$s"</string>
<string name="linked_verifying">"Verifying…"</string>
<string name="linked_verify_success">"Verified!"</string>
<string name="linked_verify_error">"Verification error!"</string>
diff --git a/OpenKeychain/src/main/res/values/styles.xml b/OpenKeychain/src/main/res/values/styles.xml
index 55c4e2220..5d7486d19 100644
--- a/OpenKeychain/src/main/res/values/styles.xml
+++ b/OpenKeychain/src/main/res/values/styles.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools">
<style name="CardViewHeader">
<item name="android:drawableBottom">?attr/cardViewHeaderDrawable</item>
@@ -38,4 +38,9 @@
<item name="android:textColor">?attr/colorAccent</item>
</style>
+ <!-- constrain size of backup code input for smaller devices -->
+ <style name="BackupCodeTextStyle">
+ <item name="android:textSize" tools:ignore="SpUsage">14dp</item>
+ </style>
+
</resources>
diff --git a/OpenKeychain/src/main/res/xml/sync_preferences.xml b/OpenKeychain/src/main/res/xml/sync_preferences.xml
index de41ff030..600ccc9e8 100644
--- a/OpenKeychain/src/main/res/xml/sync_preferences.xml
+++ b/OpenKeychain/src/main/res/xml/sync_preferences.xml
@@ -4,6 +4,12 @@
android:persistent="false"
android:title="@string/label_sync_settings_keyserver_title"/>
<SwitchPreference
+ android:key="enableWifiSyncOnly"
+ android:defaultValue="true"
+ android:persistent="true"
+ android:dependency="syncKeyserver"
+ android:title="@string/label_sync_settings_wifi_title"/>
+ <SwitchPreference
android:key="syncContacts"
android:persistent="false"
android:title="@string/label_sync_settings_contacts_title" />
diff --git a/OpenKeychain/src/main/res/xml/usb_device_filter.xml b/OpenKeychain/src/main/res/xml/usb_device_filter.xml
new file mode 100644
index 000000000..789e4ffb7
--- /dev/null
+++ b/OpenKeychain/src/main/res/xml/usb_device_filter.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Based on https://github.com/Yubico/yubikey-personalization/blob/master/ykcore/ykdef.h
+ Note that values are decimal.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Yubikey NEO OTP + CCID-->
+ <usb-device class="11" vendor-id="4176" product-id="273"/>
+ <!-- Yubikey NEO CCID-->
+ <usb-device class="11" vendor-id="4176" product-id="274"/>
+ <!-- Yubikey NEO U2F + CCID-->
+ <usb-device class="11" vendor-id="4176" product-id="277"/>
+ <!-- Yubikey NEO OTP + U2F + CCID-->
+ <usb-device class="11" vendor-id="4176" product-id="278"/>
+
+
+ <!-- Yubikey 4 CCID-->
+ <usb-device class="11" vendor-id="4176" product-id="1028"/>
+ <!-- Yubikey 4 OTP + CCID-->
+ <usb-device class="11" vendor-id="4176" product-id="1029"/>
+ <!-- Yubikey 4 U2F + CCID-->
+ <usb-device class="11" vendor-id="4176" product-id="1030"/>
+ <!-- Yubikey 4 OTP + U2F + CCID-->
+ <usb-device class="11" vendor-id="4176" product-id="1031"/>
+
+</resources> \ No newline at end of file
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
index 10ce0057b..33678ecac 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
@@ -31,6 +31,8 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
+import org.bouncycastle.bcpg.sig.KeyFlags;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -40,8 +42,6 @@ import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
-import org.bouncycastle.bcpg.sig.KeyFlags;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
@@ -124,13 +124,14 @@ public class ExportTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("snails");
- parcel.mNewUnlock = new ChangeUnlockParcel(null, new Passphrase("1234"));
+ parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase("1234"));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing());
mStaticRing2 = result.getRing();
+ mStaticRing2 = UncachedKeyRing.forTestingOnlyAddDummyLocalSignature(mStaticRing2, "1234");
}
}
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
index 19fb0345b..b87bc1cfb 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
@@ -45,7 +45,6 @@ import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
@@ -877,7 +876,7 @@ public class PgpKeyOperationTest {
long keyId = KeyringTestingHelper.getSubkeyId(ringSecurityToken, 1);
- { // moveKeyToSecurityToken should return a pending NFC_MOVE_KEY_TO_CARD result when presented with the RSA-2048
+ { // moveKeyToSecurityToken should return a pending SECURITY_TOKEN_MOVE_KEY_TO_CARD result when presented with the RSA-2048
// key, and then make key divert-to-card when it gets a serial in the cryptoInputParcel.
parcelSecurityToken.reset();
parcelSecurityToken.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
@@ -887,8 +886,8 @@ public class PgpKeyOperationTest {
PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcelSecurityToken);
Assert.assertTrue("moveKeyToSecurityToken operation should be pending", result.isPending());
- Assert.assertEquals("required input should be RequiredInputType.NFC_MOVE_KEY_TO_CARD",
- result.getRequiredInputParcel().mType, RequiredInputType.NFC_MOVE_KEY_TO_CARD);
+ Assert.assertEquals("required input should be RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD",
+ result.getRequiredInputParcel().mType, RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD);
// Create a cryptoInputParcel that matches what the SecurityTokenOperationActivity would return.
byte[] keyIdBytes = new byte[8];
@@ -921,8 +920,8 @@ public class PgpKeyOperationTest {
PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcelSecurityToken);
Assert.assertTrue("moveKeyToSecurityToken operation should be pending", result.isPending());
- Assert.assertEquals("required input should be RequiredInputType.NFC_SIGN",
- RequiredInputType.NFC_SIGN, result.getRequiredInputParcel().mType);
+ Assert.assertEquals("required input should be RequiredInputType.SECURITY_TOKEN_SIGN",
+ RequiredInputType.SECURITY_TOKEN_SIGN, result.getRequiredInputParcel().mType);
}
}
@@ -1232,47 +1231,7 @@ public class PgpKeyOperationTest {
}
@Test
- public void testUnlockPin() throws Exception {
-
- Passphrase pin = new Passphrase("5235125");
-
- // change passphrase to a pin type
- parcel.mNewUnlock = new ChangeUnlockParcel(null, pin);
- UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
-
- Assert.assertEquals("exactly three packets should have been added (the secret keys + notation packet)",
- 3, onlyA.size());
- Assert.assertEquals("exactly four packets should have been added (the secret keys + notation packet)",
- 4, onlyB.size());
-
- RawPacket dkSig = onlyB.get(1);
- Assert.assertEquals("second modified packet should be notation data",
- PacketTags.SIGNATURE, dkSig.tag);
-
- // check that notation data contains pin
- CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
- modified.getEncoded(), false, 0);
- Assert.assertEquals("secret key type should be 'pin' after this",
- SecretKeyType.PIN,
- secretRing.getSecretKey().getSecretKeyTypeSuperExpensive());
-
- // need to sleep for a sec, so the timestamp changes for notation data
- Thread.sleep(1000);
-
- {
- parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase("phrayse"), null);
- applyModificationWithChecks(parcel, modified, onlyA, onlyB, new CryptoInputParcel(pin), true, false);
-
- Assert.assertEquals("exactly four packets should have been removed (the secret keys + notation packet)",
- 4, onlyA.size());
- Assert.assertEquals("exactly three packets should have been added (no more notation packet)",
- 3, onlyB.size());
- }
-
- }
-
- @Test
- public void testRestricted () throws Exception {
+ public void testRestricted() throws Exception {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/KeyRingTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/SplitUserIdTest.java
index 64316b2a6..97feeea7b 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/KeyRingTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/SplitUserIdTest.java
@@ -28,7 +28,7 @@ import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
-public class KeyRingTest {
+public class SplitUserIdTest {
@Test
public void splitCompleteUserIdShouldReturnAll3Components() throws Exception {
@@ -49,9 +49,40 @@ public class KeyRingTest {
@Test
public void splitUserIdWithAllButEmailShouldReturnNameAndComment() throws Exception {
KeyRing.UserId info = KeyRing.splitUserId("Max Mustermann (this is a comment)");
- Assert.assertEquals(info.name, "Max Mustermann");
- Assert.assertEquals(info.comment, "this is a comment");
+ Assert.assertEquals("Max Mustermann", info.name);
+ Assert.assertEquals("this is a comment", info.comment);
+ Assert.assertNull(info.email);
+ }
+
+ @Test
+ public void splitUserIdWithCommentAndEmailShouldReturnCommentAndEmail() throws Exception {
+ KeyRing.UserId info = KeyRing.splitUserId(" (this is a comment) <max@example.com>");
+ Assert.assertNull(info.name);
+ Assert.assertEquals("this is a comment", info.comment);
+ Assert.assertEquals("max@example.com", info.email);
+ }
+
+ @Test
+ public void splitUserIdWithEmailShouldReturnEmail() throws Exception {
+ KeyRing.UserId info = KeyRing.splitUserId("max@example.com");
+ Assert.assertNull(info.name);
+ Assert.assertNull(info.comment);
+ Assert.assertEquals("max@example.com", info.email);
+ }
+
+ @Test
+ public void splitUserIdWithNameShouldReturnName() throws Exception {
+ KeyRing.UserId info = KeyRing.splitUserId("Max Mustermann");
+ Assert.assertEquals("Max Mustermann", info.name);
+ Assert.assertNull(info.comment);
Assert.assertNull(info.email);
}
+ @Test
+ public void splitUserIdWithCommentShouldReturnComment() throws Exception {
+ KeyRing.UserId info = KeyRing.splitUserId(" (this is a comment)");
+ Assert.assertNull(info.name);
+ Assert.assertEquals("this is a comment", info.comment);
+ Assert.assertNull(info.email);
+ }
} \ No newline at end of file
diff --git a/README.md b/README.md
index d7a07e232..0ebba655a 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@ Development mailinglist at https://lists.riseup.net/www/subscribe/openkeychain
3. Have Android SDK "tools", "platform-tools", and "build-tools" directories in your PATH (http://developer.android.com/sdk/index.html)
4. Open the Android SDK Manager (shell command: ``android``).
Expand the Tools directory and select "Android SDK Build-tools (Version 23.0.1)".
-Expand the Extras directory and install "Android Support Repository"
+Expand the Extras directory and install "Android Support Library", as well as "Local Maven repository for Support Libraries"
Select SDK Platform for API levels 21, 22, and 23.
5. Export ANDROID_HOME pointing to your Android SDK
6. Execute ``./gradlew assembleFdroidDebug``
@@ -74,7 +74,7 @@ We are using the newest [Android Studio](http://developer.android.com/sdk/instal
OpenKeychain uses a forked version with some small changes. These changes will been sent to Bouncy Castle.
see
-* Fork: https://github.com/openpgp-keychain/bouncycastle
+* Fork: https://github.com/open-keychain/bouncycastle
#### Bouncy Castle resources
@@ -110,6 +110,9 @@ ext {
```
* Change SDK and Build Tools in git submodules "openkeychain-api-lib" and "openpgp-api-lib" manually. They should also build on their own without the ext variables.
+#### Update library
+* You can check for library updates with ``./gradlew dependencyUpdates -Drevision=release
+
#### Add new library
* You can add the library as a Maven dependency or as a git submodule (if patches are required) in the "extern" folder.
* You can get all transitive dependencies with ``./gradlew -q dependencies OpenKeychain:dependencies``
diff --git a/build.gradle b/build.gradle
index 0f2e968b2..dc1c4960b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,16 +7,15 @@ buildscript {
dependencies {
// NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information
- classpath 'com.android.tools.build:gradle:1.5.0'
+ classpath 'com.android.tools.build:gradle:2.1.0'
classpath files('gradle-witness.jar')
// bintray dependency to satisfy dependency of openpgp-api lib
classpath 'com.novoda:bintray-release:0.2.7'
-
- classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.4.0'
- // classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.0.2'
- // allows to check for lib updates with "gradle dependencyUpdates -Drevision=release"
- classpath 'com.github.ben-manes:gradle-versions-plugin:0.11.3'
+ classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.6.3'
+
+ // allows to check for lib updates with "./gradlew dependencyUpdates -Drevision=release"
+ classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0'
}
}
@@ -27,7 +26,7 @@ allprojects {
}
task wrapper(type: Wrapper) {
- gradleVersion = '2.5'
+ gradleVersion = '2.12'
}
subprojects {
@@ -49,5 +48,5 @@ project(':extern:bouncycastle') {
// See http://tools.android.com/tech-docs/new-build-system/tips#TOC-Controlling-Android-properties-of-all-your-modules-from-the-main-project.
ext {
compileSdkVersion = 23
- buildToolsVersion = '23.0.1'
+ buildToolsVersion = '23.0.2'
}
diff --git a/extern/KeybaseLib b/extern/KeybaseLib
-Subproject bc02742a59f4cc984cd497e14ac48cb61fe6e8c
+Subproject 0a99c130e6d1bc25a313a113d7f7777bf65b2e3
diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib
-Subproject 8ca0f578bb843db7744dbd5724b32f6664b5c3d
+Subproject f027645214ff41a54e15cc46058ce9f1867cad5
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index cb5ccd9f1..2c6137b87 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4828f29e8..8ef44dad4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
-#Mon Aug 03 18:56:28 CEST 2015
+#Tue Apr 12 19:22:55 CEST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip
-distributionSha256Sum=b71ab21fa5e91dcc6a4bd723b13403e8610a6e1b4b9d4b314ff477820de00bf9 \ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip
+distributionSha256Sum=d8b1948a575dc9ec13e03db94502ce91815d73da023f611296c04b852164cb5f \ No newline at end of file
diff --git a/gradlew b/gradlew
index 91a7e269e..9d82f7891 100755
--- a/gradlew
+++ b/gradlew
@@ -42,11 +42,6 @@ case "`uname`" in
;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -61,9 +56,9 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
+cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
+cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -114,6 +109,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
diff --git a/gradlew.bat b/gradlew.bat
index aec99730b..72d362daf 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -46,7 +46,7 @@ echo location of your Java installation.
goto fail
:init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
diff --git a/settings.gradle b/settings.gradle
index 2d8a34da7..2244f1c3d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,9 +1,19 @@
include ':OpenKeychain'
-include ':extern:openpgp-api-lib:openpgp-api'
-include ':extern:openkeychain-api-lib:openkeychain-intents'
include ':extern:bouncycastle:core'
include ':extern:bouncycastle:pg'
include ':extern:bouncycastle:prov'
include ':extern:minidns'
-include ':extern:KeybaseLib:Lib'
-include ':extern:safeslinger-exchange:safeslinger-exchange'
+
+// Workaround for Android Gradle Plugin 2.0, as described in http://stackoverflow.com/a/36544850
+//include ':extern:safeslinger-exchange:safeslinger-exchange'
+include ':safeslinger-exchange'
+project(':safeslinger-exchange').projectDir = new File('extern/safeslinger-exchange/safeslinger-exchange')
+//include ':extern:openkeychain-api-lib:openkeychain-intents'
+include ':openkeychain-api-lib'
+project(':openkeychain-api-lib').projectDir = new File('extern/openkeychain-api-lib/openkeychain-intents')
+//include ':extern:openpgp-api-lib:openpgp-api'
+include ':openpgp-api-lib'
+project(':openpgp-api-lib').projectDir = new File('extern/openpgp-api-lib/openpgp-api')
+//include ':extern:KeybaseLib:Lib'
+include ':KeybaseLib'
+project(':KeybaseLib').projectDir = new File('extern/KeybaseLib/Lib') \ No newline at end of file