aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore21
-rw-r--r--.travis.yml2
-rw-r--r--.tx/config2
-rw-r--r--OpenKeychain/build.gradle181
-rw-r--r--OpenKeychain/proguard-rules.pro41
-rw-r--r--OpenKeychain/src/main/AndroidManifest.xml85
-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.java25
-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/ChangeUnlockOperation.java108
-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.java5
-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.java178
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java38
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java42
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java261
-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/KeychainExternalContract.java52
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java21
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java220
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/SimpleContentResolverInterface.java39
-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/ApiPermissionHelper.java75
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java284
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java194
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteCreateAccountActivity.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterActivity.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdListFragment.java9
-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.java756
-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/ChangeUnlockParcel.java88
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java3
-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.java75
-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.java201
-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.java64
-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.java56
-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.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java102
-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.java20
-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.java7
-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/RedirectImportKeysActivity.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java180
-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.java61
-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/ImportKeysListLoader.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java13
-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/BaseSecurityTokenActivity.java513
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java1057
-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.java24
-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-cs/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-cs/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_changelog.md9
-rw-r--r--OpenKeychain/src/main/res/raw-es-rMX/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-es-rMX/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-es/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-es/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-eu/advanced.md14
-rw-r--r--OpenKeychain/src/main/res/raw-eu/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-eu/help_changelog.md13
-rw-r--r--OpenKeychain/src/main/res/raw-fa/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-fa/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-fi/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-fi/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_changelog.md9
-rw-r--r--OpenKeychain/src/main/res/raw-hi/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-hi/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-hu/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-hu/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-id/advanced.md12
-rw-r--r--OpenKeychain/src/main/res/raw-id/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-id/help_changelog.md77
-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.md31
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-kn/advanced.md9
-rw-r--r--OpenKeychain/src/main/res/raw-kn/help_about.md72
-rw-r--r--OpenKeychain/src/main/res/raw-kn/help_certification.md28
-rw-r--r--OpenKeychain/src/main/res/raw-kn/help_changelog.md326
-rw-r--r--OpenKeychain/src/main/res/raw-kn/help_start.md16
-rw-r--r--OpenKeychain/src/main/res/raw-ko/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-ko/help_changelog.md7
-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.md9
-rw-r--r--OpenKeychain/src/main/res/raw-nb/help_about.md72
-rw-r--r--OpenKeychain/src/main/res/raw-nb/help_certification.md28
-rw-r--r--OpenKeychain/src/main/res/raw-nb/help_changelog.md326
-rw-r--r--OpenKeychain/src/main/res/raw-nb/help_start.md16
-rw-r--r--OpenKeychain/src/main/res/raw-nl/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-nl/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-pl/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-pl/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/advanced.md9
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/help_about.md72
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/help_certification.md28
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/help_changelog.md326
-rw-r--r--OpenKeychain/src/main/res/raw-pt-rBR/help_start.md16
-rw-r--r--OpenKeychain/src/main/res/raw-ru/advanced.md12
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_about.md8
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_certification.md12
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_changelog.md17
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_start.md2
-rw-r--r--OpenKeychain/src/main/res/raw-sl/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-sl/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-sr/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-sr/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-sv/advanced.md2
-rw-r--r--OpenKeychain/src/main/res/raw-sv/help_about.md4
-rw-r--r--OpenKeychain/src/main/res/raw-sv/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-tr/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-tr/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-uk/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-uk/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-vi/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-vi/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-zh-rTW/advanced.md14
-rw-r--r--OpenKeychain/src/main/res/raw-zh-rTW/help_about.md2
-rw-r--r--OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw-zh/advanced.md14
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_about.md10
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_certification.md32
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_changelog.md23
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_start.md18
-rw-r--r--OpenKeychain/src/main/res/raw/help_changelog.md7
-rw-r--r--OpenKeychain/src/main/res/raw/help_faq.md2
-rw-r--r--OpenKeychain/src/main/res/values-cs/strings.xml335
-rw-r--r--OpenKeychain/src/main/res/values-de/strings.xml484
-rw-r--r--OpenKeychain/src/main/res/values-es-rMX/strings.xml11
-rw-r--r--OpenKeychain/src/main/res/values-es/strings.xml24
-rw-r--r--OpenKeychain/src/main/res/values-eu/strings.xml130
-rw-r--r--OpenKeychain/src/main/res/values-fa/strings.xml6
-rw-r--r--OpenKeychain/src/main/res/values-fi/strings.xml10
-rw-r--r--OpenKeychain/src/main/res/values-fr/strings.xml91
-rw-r--r--OpenKeychain/src/main/res/values-hi/strings.xml18
-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.xml145
-rw-r--r--OpenKeychain/src/main/res/values-ja/strings.xml72
-rw-r--r--OpenKeychain/src/main/res/values-kn/strings.xml87
-rw-r--r--OpenKeychain/src/main/res/values-ko/strings.xml11
-rw-r--r--OpenKeychain/src/main/res/values-la/strings.xml87
-rw-r--r--OpenKeychain/src/main/res/values-nb/strings.xml203
-rw-r--r--OpenKeychain/src/main/res/values-nl/strings.xml22
-rw-r--r--OpenKeychain/src/main/res/values-pl/strings.xml17
-rw-r--r--OpenKeychain/src/main/res/values-pt-rBR/strings.xml493
-rw-r--r--OpenKeychain/src/main/res/values-ru/strings.xml782
-rw-r--r--OpenKeychain/src/main/res/values-sl/strings.xml17
-rw-r--r--OpenKeychain/src/main/res/values-sr/strings.xml53
-rw-r--r--OpenKeychain/src/main/res/values-sv/strings.xml59
-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.xml12
-rw-r--r--OpenKeychain/src/main/res/values-uk/strings.xml16
-rw-r--r--OpenKeychain/src/main/res/values-vi/strings.xml1
-rw-r--r--OpenKeychain/src/main/res/values-zh-rTW/strings.xml15
-rw-r--r--OpenKeychain/src/main/res/values-zh/strings.xml189
-rw-r--r--OpenKeychain/src/main/res/values/colors.xml1
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml79
-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/BenchmarkOperationTest.java1
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java6
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java11
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java4
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java6
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java77
-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--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java4
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java6
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java4
-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
272 files changed, 12274 insertions, 4349 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/.tx/config b/.tx/config
index 4ea2ec29f..de1ec1ed0 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,6 +1,6 @@
[main]
host = https://www.transifex.com
-lang_map = he: iw, zh_TW: zh-rTW, es_MX: es-rMX
+lang_map = he: iw, zh_TW: zh-rTW, es_MX: es-rMX, pt_BR: pt-rBR
[open-keychain.strings]
file_filter = OpenKeychain/src/main/res/values-<lang>/strings.xml
diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle
index 01e221aca..4d1b572aa 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 38200
- versionName "3.8.2"
+ versionCode 39600
+ versionName "3.9.6 beta"
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 e1cccef1f..bead69c58 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -5,11 +5,6 @@
android:installLocation="auto">
<!--
- General remarks
- ===============
- - Last APG 1 version was 10900 (1.0.9 beta 00)
- - Keychain starting with versionCode 20000!
-
Association of file types to Keychain
=====================================
General remarks about file ending conventions:
@@ -48,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"
@@ -81,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" />
@@ -94,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"
@@ -503,21 +513,6 @@
android:theme="@style/Theme.Keychain.Transparent"
android:windowSoftInputMode="stateHidden">
- <!-- VIEW with fingerprint scheme:
- Handle URIs with fingerprints when scanning directly from Barcode Scanner -->
- <intent-filter android:label="@string/intent_import_key">
- <action android:name="android.intent.action.VIEW" />
-
- <category android:name="android.intent.category.BROWSABLE" />
- <category android:name="android.intent.category.DEFAULT" />
-
- <!-- Android's scheme matcher is case-sensitive, so include most likely variations -->
- <data android:scheme="openpgp4fpr" />
- <data android:scheme="OPENPGP4FPR" />
- <data android:scheme="OpenPGP4FPR" />
- <data android:scheme="OpenPGP4Fpr" />
- <data android:scheme="OpenPGP4fpr" />
- </intent-filter>
<!-- IMPORT_KEY without mimeType to allow import with extras Bundle -->
<intent-filter android:label="@string/intent_import_key">
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE" />
@@ -730,11 +725,36 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
+
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.MainActivity" />
</activity>
<activity
+ android:name=".ui.RedirectImportKeysActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
+ android:label="@string/title_activity_redirect_key"
+ android:theme="@style/Theme.Keychain.Transparent">
+
+ <!-- VIEW with fingerprint scheme:
+ Handle URIs with fingerprints when scanning directly from Barcode Scanner -->
+ <intent-filter android:label="@string/intent_import_key">
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+
+ <!-- Android's scheme matcher is case-sensitive, so include most likely variations -->
+ <data android:scheme="openpgp4fpr" />
+ <data android:scheme="OPENPGP4FPR" />
+ <data android:scheme="OpenPGP4FPR" />
+ <data android:scheme="OpenPGP4Fpr" />
+ <data android:scheme="OpenPGP4fpr" />
+ </intent-filter>
+ </activity>
+
+
+ <activity
android:name=".ui.LogDisplayActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_log_display" />
@@ -806,7 +826,12 @@
android:exported="false"
android:label="@string/keyserver_sync_settings_title" />
- <!-- Internal classes of the remote APIs (not exported!) -->
+ <provider
+ android:name=".remote.KeychainExternalProvider"
+ android:authorities="${applicationId}.provider.exported"
+ android:exported="true" />
+
+ <!-- Internal classes of the remote APIs (not exported) -->
<activity
android:name=".remote.ui.RemoteCreateAccountActivity"
android:exported="false"
@@ -864,6 +889,24 @@
android:configChanges="keyboardHidden|keyboard"
android:label="@string/title_backup" />
+ <!-- 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
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 d87a82a24..6217d1a01 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java
@@ -21,11 +21,12 @@ package org.sufficientlysecure.keychain.keyimport;
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;
@@ -33,11 +34,12 @@ 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;
-import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@@ -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. ");
}
}
@@ -190,8 +194,11 @@ public class FacebookKeyserver extends Keyserver {
return uri.getPathSegments().get(0);
}
- public static boolean isFacebookHost(Uri uri) {
+ public static boolean isFacebookHost(@Nullable Uri uri) {
+ if (uri == null) {
+ return false;
+ }
String host = uri.getHost();
- return host.equalsIgnoreCase(FB_HOST) || host.equalsIgnoreCase(FB_HOST_WWW);
+ return FB_HOST.equalsIgnoreCase(host) || FB_HOST_WWW.equalsIgnoreCase(host);
}
}
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/ChangeUnlockOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ChangeUnlockOperation.java
new file mode 100644
index 000000000..f9ae13b1a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ChangeUnlockOperation.java
@@ -0,0 +1,108 @@
+/*
+ * 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.operations;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+
+public class ChangeUnlockOperation extends BaseOperation<ChangeUnlockParcel> {
+
+ public ChangeUnlockOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ @NonNull
+ public OperationResult execute(ChangeUnlockParcel unlockParcel, CryptoInputParcel cryptoInput) {
+ OperationResult.OperationLog log = new OperationResult.OperationLog();
+ log.add(OperationResult.LogType.MSG_ED, 0);
+
+ if (unlockParcel == null || unlockParcel.mMasterKeyId == null) {
+ log.add(OperationResult.LogType.MSG_ED_ERROR_NO_PARCEL, 1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // Perform actual modification
+ PgpEditKeyResult modifyResult;
+ {
+ PgpKeyOperation keyOperations =
+ new PgpKeyOperation(new ProgressScaler(mProgressable, 0, 70, 100));
+
+ try {
+ log.add(OperationResult.LogType.MSG_ED_FETCHING, 1,
+ KeyFormattingUtils.convertKeyIdToHex(unlockParcel.mMasterKeyId));
+
+ CanonicalizedSecretKeyRing secRing =
+ mProviderHelper.getCanonicalizedSecretKeyRing(unlockParcel.mMasterKeyId);
+ modifyResult = keyOperations.modifyKeyRingPassphrase(secRing, cryptoInput, unlockParcel);
+
+ if (modifyResult.isPending()) {
+ // obtain original passphrase from user
+ log.add(modifyResult, 1);
+ return new EditKeyResult(log, modifyResult);
+ }
+ } catch (ProviderHelper.NotFoundException e) {
+ log.add(OperationResult.LogType.MSG_ED_ERROR_KEY_NOT_FOUND, 2);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+ }
+
+ log.add(modifyResult, 1);
+
+ if (!modifyResult.success()) {
+ // error is already logged by modification
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // Cannot cancel from here on out!
+ mProgressable.setPreventCancel();
+
+ // It's a success, so this must be non-null now
+ UncachedKeyRing ring = modifyResult.getRing();
+
+ SaveKeyringResult saveResult = mProviderHelper
+ .saveSecretKeyRing(ring, new ProgressScaler(mProgressable, 70, 95, 100));
+ log.add(saveResult, 1);
+
+ // If the save operation didn't succeed, exit here
+ if (!saveResult.success()) {
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ updateProgress(R.string.progress_done, 100, 100);
+ log.add(OperationResult.LogType.MSG_ED_SUCCESS, 0);
+ return new EditKeyResult(EditKeyResult.RESULT_OK, log, ring.getMasterKeyId());
+
+ }
+
+}
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..d3d962808 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
@@ -539,6 +539,7 @@ public abstract class OperationResult implements Parcelable {
// secret key modify
MSG_MF (LogLevel.START, R.string.msg_mr),
MSG_MF_DIVERT (LogLevel.DEBUG, R.string.msg_mf_divert),
+ MSG_MF_ERROR_ALL_KEYS_STRIPPED (LogLevel.ERROR, R.string.msg_mf_error_all_keys_stripped),
MSG_MF_ERROR_DIVERT_NEWSUB (LogLevel.ERROR, R.string.msg_mf_error_divert_newsub),
MSG_MF_ERROR_DIVERT_SERIAL (LogLevel.ERROR, R.string.msg_mf_error_divert_serial),
MSG_MF_ERROR_ENCODE (LogLevel.ERROR, R.string.msg_mf_error_encode),
@@ -552,6 +553,7 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_ERROR_NOOP (LogLevel.ERROR, R.string.msg_mf_error_noop),
MSG_MF_ERROR_NULL_EXPIRY (LogLevel.ERROR, R.string.msg_mf_error_null_expiry),
MSG_MF_ERROR_PASSPHRASE_MASTER(LogLevel.ERROR, R.string.msg_mf_error_passphrase_master),
+ MSG_MF_ERROR_PASSPHRASES_UNCHANGED(LogLevel.ERROR, R.string.msg_mf_error_passphrases_unchanged),
MSG_MF_ERROR_PAST_EXPIRY(LogLevel.ERROR, R.string.msg_mf_error_past_expiry),
MSG_MF_ERROR_PGP (LogLevel.ERROR, R.string.msg_mf_error_pgp),
MSG_MF_ERROR_RESTRICTED(LogLevel.ERROR, R.string.msg_mf_error_restricted),
@@ -566,8 +568,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),
@@ -730,6 +730,7 @@ public abstract class OperationResult implements Parcelable {
MSG_PSE_ERROR_PGP (LogLevel.ERROR, R.string.msg_pse_error_pgp),
MSG_PSE_ERROR_SIG (LogLevel.ERROR, R.string.msg_pse_error_sig),
MSG_PSE_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_pse_error_unlock),
+ MSG_PSE_ERROR_REVOKED_OR_EXPIRED (LogLevel.ERROR, R.string.msg_pse_error_revoked_or_expired),
MSG_PSE_KEY_OK (LogLevel.OK, R.string.msg_pse_key_ok),
MSG_PSE_KEY_UNKNOWN (LogLevel.DEBUG, R.string.msg_pse_key_unknown),
MSG_PSE_KEY_WARN (LogLevel.WARN, R.string.msg_pse_key_warn),
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..1ebab7847 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.name);
}
- 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..404e07230 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;
@@ -55,15 +72,15 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult;
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.service.ChangeUnlockParcel;
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 +88,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 +497,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);
@@ -1053,13 +1054,13 @@ public class PgpKeyOperation {
}
// 6. If requested, change passphrase
- if (saveParcel.mNewUnlock != null) {
+ if (saveParcel.getChangeUnlockParcel() != null) {
progress(R.string.progress_modify_passphrase, 90);
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.getChangeUnlockParcel().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);
@@ -1192,73 +1193,80 @@ public class PgpKeyOperation {
}
- private static PGPSecretKeyRing applyNewUnlock(
- PGPSecretKeyRing sKR,
- PGPPublicKey masterPublicKey,
- PGPPrivateKey masterPrivateKey,
- Passphrase passphrase,
- ChangeUnlockParcel newUnlock,
- OperationLog log, int indent) throws PGPException {
+ public PgpEditKeyResult modifyKeyRingPassphrase(CanonicalizedSecretKeyRing wsKR,
+ CryptoInputParcel cryptoInput,
+ ChangeUnlockParcel changeUnlockParcel) {
- 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)) {
+ OperationLog log = new OperationLog();
+ int indent = 0;
- log.add(LogType.MSG_MF_NOTATION_EMPTY, indent);
+ if (changeUnlockParcel.mMasterKeyId == null || changeUnlockParcel.mMasterKeyId != wsKR.getMasterKeyId()) {
+ log.add(LogType.MSG_MF_ERROR_KEYID, indent);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
- // 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);
+ log.add(LogType.MSG_MF, indent,
+ KeyFormattingUtils.convertKeyIdToHex(wsKR.getMasterKeyId()));
+ indent += 1;
+ progress(R.string.progress_building_key, 0);
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
- sKR = PGPSecretKeyRing.insertSecretKey(sKR,
- PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey));
- }
+ // We work on bouncycastle object level here
+ PGPSecretKeyRing sKR = wsKR.getRing();
+ PGPSecretKey masterSecretKey = sKR.getSecretKey();
+ PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
+ // Make sure the fingerprint matches
+ if (changeUnlockParcel.mFingerprint == null || !Arrays.equals(changeUnlockParcel.mFingerprint,
+ masterSecretKey.getPublicKey().getFingerprint())) {
+ log.add(LogType.MSG_MF_ERROR_FINGERPRINT, indent);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
- return sKR;
+ // Find the first unstripped secret key
+ PGPSecretKey nonDummy = firstNonDummySecretKeyID(sKR);
+ if(nonDummy == null) {
+ log.add(OperationResult.LogType.MSG_MF_ERROR_ALL_KEYS_STRIPPED, indent);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
- if (newUnlock.mNewPin != null) {
- sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPin, log, indent);
+ if (!cryptoInput.hasPassphrase()) {
+ log.add(LogType.MSG_MF_REQUIRE_PASSPHRASE, indent);
- log.add(LogType.MSG_MF_NOTATION_PIN, indent);
+ return new PgpEditKeyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
+ masterSecretKey.getKeyID(), nonDummy.getKeyID(),
+ cryptoInput.getSignatureTime()), cryptoInput);
+ } else {
+ progress(R.string.progress_modify_passphrase, 50);
+ log.add(LogType.MSG_MF_PASSPHRASE, indent);
+ indent += 1;
- // 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());
+ try {
+ sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(),
+ changeUnlockParcel.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);
+ }
+ } catch (PGPException e) {
+ throw new UnsupportedOperationException("Failed to build encryptor/decryptor!");
}
- 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;
+ indent -= 1;
+ progress(R.string.progress_done, 100);
+ log.add(LogType.MSG_MF_SUCCESS, indent);
+ return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR));
}
+ }
- throw new UnsupportedOperationException("PIN passphrases not yet implemented!");
+ private static PGPSecretKey firstNonDummySecretKeyID(PGPSecretKeyRing secRing) {
+ Iterator<PGPSecretKey> secretKeyIterator = secRing.getSecretKeys();
+ while(secretKeyIterator.hasNext()) {
+ PGPSecretKey secretKey = secretKeyIterator.next();
+ if(!isDummy(secretKey)){
+ return secretKey;
+ }
+ }
+ return null;
}
/** This method returns true iff the provided keyring has a local direct key signature
@@ -1293,9 +1301,9 @@ public class PgpKeyOperation {
PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(newPassphrase.getCharArray());
+ boolean keysModified = false;
- // 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()));
@@ -1307,8 +1315,8 @@ public class PgpKeyOperation {
ok = true;
} catch (PGPException e) {
- // if this is the master key, error!
- if (sKey.getKeyID() == masterPublicKey.getKeyID()) {
+ // if the master key failed && it's not stripped, error!
+ if (sKey.getKeyID() == masterPublicKey.getKeyID() && !isDummy(sKey)) {
log.add(LogType.MSG_MF_ERROR_PASSPHRASE_MASTER, indent+1);
return null;
}
@@ -1335,7 +1343,13 @@ public class PgpKeyOperation {
}
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+ keysModified = true;
+ }
+ if(!keysModified) {
+ // no passphrase was changed
+ log.add(LogType.MSG_MF_ERROR_PASSPHRASES_UNCHANGED, indent+1);
+ return null;
}
return sKR;
@@ -1348,7 +1362,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/PgpSignEncryptInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
index 580103942..8eae92e63 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
@@ -38,7 +38,6 @@ public class PgpSignEncryptInputParcel implements Parcelable {
protected Long mSignatureSubKeyId = null;
protected int mSignatureHashAlgorithm = PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT;
protected long mAdditionalEncryptId = Constants.key.none;
- protected boolean mFailOnMissingEncryptionKeyIds = false;
protected String mCharset;
protected boolean mCleartextSignature;
protected boolean mDetachedSignature = false;
@@ -65,7 +64,6 @@ public class PgpSignEncryptInputParcel implements Parcelable {
mSignatureSubKeyId = source.readInt() == 1 ? source.readLong() : null;
mSignatureHashAlgorithm = source.readInt();
mAdditionalEncryptId = source.readLong();
- mFailOnMissingEncryptionKeyIds = source.readInt() == 1;
mCharset = source.readString();
mCleartextSignature = source.readInt() == 1;
mDetachedSignature = source.readInt() == 1;
@@ -96,7 +94,6 @@ public class PgpSignEncryptInputParcel implements Parcelable {
}
dest.writeInt(mSignatureHashAlgorithm);
dest.writeLong(mAdditionalEncryptId);
- dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0);
dest.writeString(mCharset);
dest.writeInt(mCleartextSignature ? 1 : 0);
dest.writeInt(mDetachedSignature ? 1 : 0);
@@ -113,10 +110,6 @@ public class PgpSignEncryptInputParcel implements Parcelable {
this.mCharset = mCharset;
}
- public boolean isFailOnMissingEncryptionKeyIds() {
- return mFailOnMissingEncryptionKeyIds;
- }
-
public long getAdditionalEncryptId() {
return mAdditionalEncryptId;
}
@@ -207,11 +200,6 @@ public class PgpSignEncryptInputParcel implements Parcelable {
return this;
}
- public PgpSignEncryptInputParcel setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) {
- mFailOnMissingEncryptionKeyIds = failOnMissingEncryptionKeyIds;
- return this;
- }
-
public PgpSignEncryptInputParcel setCleartextSignature(boolean cleartextSignature) {
this.mCleartextSignature = cleartextSignature;
return this;
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..0bb6419eb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
@@ -168,10 +168,17 @@ public class PgpSignEncryptOperation extends BaseOperation {
try {
long signingMasterKeyId = input.getSignatureMasterKeyId();
long signingSubKeyId = input.getSignatureSubKeyId();
- {
- CanonicalizedSecretKeyRing signingKeyRing =
- mProviderHelper.getCanonicalizedSecretKeyRing(signingMasterKeyId);
- signingKey = signingKeyRing.getSecretKey(input.getSignatureSubKeyId());
+
+ CanonicalizedSecretKeyRing signingKeyRing =
+ mProviderHelper.getCanonicalizedSecretKeyRing(signingMasterKeyId);
+ signingKey = signingKeyRing.getSecretKey(input.getSignatureSubKeyId());
+
+
+ // Make sure key is not expired or revoked
+ if (signingKeyRing.isExpired() || signingKeyRing.isRevoked()
+ || signingKey.isExpired() || signingKey.isRevoked()) {
+ log.add(LogType.MSG_PSE_ERROR_REVOKED_OR_EXPIRED, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
// Make sure we are allowed to sign here!
@@ -281,16 +288,17 @@ public class PgpSignEncryptOperation extends BaseOperation {
if (encryptSubKeyIds.isEmpty()) {
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
- if (input.isFailOnMissingEncryptionKeyIds()) {
- return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
- }
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
+ }
+ // Make sure key is not expired or revoked
+ if (keyRing.isExpired() || keyRing.isRevoked()) {
+ log.add(LogType.MSG_PSE_ERROR_REVOKED_OR_EXPIRED, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
} catch (ProviderHelper.NotFoundException e) {
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
- if (input.isFailOnMissingEncryptionKeyIds()) {
- return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
- }
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
}
}
@@ -470,7 +478,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 +528,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/ApiDataAccessObject.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java
new file mode 100644
index 000000000..7c8295ad5
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014-2016 Vincent Breitmoser <v.breitmoser@mugenguild.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.provider;
+
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+
+import org.bouncycastle.bcpg.CompressionAlgorithmTags;
+import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.remote.AppSettings;
+
+
+public class ApiDataAccessObject {
+
+ private final SimpleContentResolverInterface mQueryInterface;
+
+ public ApiDataAccessObject(Context context) {
+ final ContentResolver contentResolver = context.getContentResolver();
+ mQueryInterface = new SimpleContentResolverInterface() {
+ @Override
+ public Cursor query(Uri contentUri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return contentResolver.query(contentUri, projection, selection, selectionArgs, sortOrder);
+ }
+
+ @Override
+ public Uri insert(Uri contentUri, ContentValues values) {
+ return contentResolver.insert(contentUri, values);
+ }
+
+ @Override
+ public int update(Uri contentUri, ContentValues values, String where, String[] selectionArgs) {
+ return contentResolver.update(contentUri, values, where, selectionArgs);
+ }
+
+ @Override
+ public int delete(Uri contentUri, String where, String[] selectionArgs) {
+ return contentResolver.delete(contentUri, where, selectionArgs);
+ }
+ };
+ }
+
+ public ApiDataAccessObject(SimpleContentResolverInterface queryInterface) {
+ mQueryInterface = queryInterface;
+ }
+
+ public ArrayList<String> getRegisteredApiApps() {
+ Cursor cursor = mQueryInterface.query(ApiApps.CONTENT_URI, null, null, null, null);
+
+ ArrayList<String> packageNames = new ArrayList<>();
+ try {
+ if (cursor != null) {
+ int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
+ if (cursor.moveToFirst()) {
+ do {
+ packageNames.add(cursor.getString(packageNameCol));
+ } while (cursor.moveToNext());
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return packageNames;
+ }
+
+ private ContentValues contentValueForApiApps(AppSettings appSettings) {
+ ContentValues values = new ContentValues();
+ values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
+ values.put(ApiApps.PACKAGE_CERTIFICATE, appSettings.getPackageCertificate());
+ return values;
+ }
+
+ private ContentValues contentValueForApiAccounts(AccountSettings accSettings) {
+ ContentValues values = new ContentValues();
+ values.put(KeychainContract.ApiAccounts.ACCOUNT_NAME, accSettings.getAccountName());
+ values.put(KeychainContract.ApiAccounts.KEY_ID, accSettings.getKeyId());
+
+ // DEPRECATED and thus hardcoded
+ values.put(KeychainContract.ApiAccounts.COMPRESSION, CompressionAlgorithmTags.ZLIB);
+ values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM,
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
+ values.put(KeychainContract.ApiAccounts.HASH_ALORITHM,
+ PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT);
+ return values;
+ }
+
+ public void insertApiApp(AppSettings appSettings) {
+ mQueryInterface.insert(ApiApps.CONTENT_URI,
+ contentValueForApiApps(appSettings));
+ }
+
+ public void insertApiAccount(Uri uri, AccountSettings accSettings) {
+ mQueryInterface.insert(uri, contentValueForApiAccounts(accSettings));
+ }
+
+ public void updateApiAccount(Uri uri, AccountSettings accSettings) {
+ if (mQueryInterface.update(uri, contentValueForApiAccounts(accSettings), null,
+ null) <= 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Must be an uri pointing to an account
+ */
+ public AppSettings getApiAppSettings(Uri uri) {
+ AppSettings settings = null;
+
+ Cursor cursor = mQueryInterface.query(uri, null, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ settings = new AppSettings();
+ settings.setPackageName(cursor.getString(
+ cursor.getColumnIndex(ApiApps.PACKAGE_NAME)));
+ settings.setPackageCertificate(cursor.getBlob(
+ cursor.getColumnIndex(ApiApps.PACKAGE_CERTIFICATE)));
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return settings;
+ }
+
+ public AccountSettings getApiAccountSettings(Uri accountUri) {
+ AccountSettings settings = null;
+
+ Cursor cursor = mQueryInterface.query(accountUri, null, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ settings = new AccountSettings();
+
+ settings.setAccountName(cursor.getString(
+ cursor.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
+ settings.setKeyId(cursor.getLong(
+ cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return settings;
+ }
+
+ public Set<Long> getAllKeyIdsForApp(Uri uri) {
+ Set<Long> keyIds = new HashSet<>();
+
+ Cursor cursor = mQueryInterface.query(uri, null, null, null, null);
+ try {
+ if (cursor != null) {
+ int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID);
+ while (cursor.moveToNext()) {
+ keyIds.add(cursor.getLong(keyIdColumn));
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return keyIds;
+ }
+
+ public HashSet<Long> getAllowedKeyIdsForApp(Uri uri) {
+ HashSet<Long> keyIds = new HashSet<>();
+
+ Cursor cursor = mQueryInterface.query(uri, null, null, null, null);
+ try {
+ if (cursor != null) {
+ int keyIdColumn = cursor.getColumnIndex(ApiAllowedKeys.KEY_ID);
+ while (cursor.moveToNext()) {
+ keyIds.add(cursor.getLong(keyIdColumn));
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return keyIds;
+ }
+
+ public void saveAllowedKeyIdsForApp(Uri uri, Set<Long> allowedKeyIds)
+ throws RemoteException, OperationApplicationException {
+ // wipe whole table of allowed keys for this account
+ mQueryInterface.delete(uri, null, null);
+
+ // re-insert allowed key ids
+ for (Long keyId : allowedKeyIds) {
+ ContentValues values = new ContentValues();
+ values.put(ApiAllowedKeys.KEY_ID, keyId);
+ mQueryInterface.insert(uri, values);
+ }
+ }
+
+ public void addAllowedKeyIdForApp(Uri uri, long allowedKeyId) {
+ ContentValues values = new ContentValues();
+ values.put(ApiAllowedKeys.KEY_ID, allowedKeyId);
+ mQueryInterface.insert(uri, values);
+ }
+
+ public byte[] getApiAppCertificate(String packageName) {
+ Uri queryUri = ApiApps.buildByPackageNameUri(packageName);
+
+ String[] projection = new String[]{ApiApps.PACKAGE_CERTIFICATE};
+
+ Cursor cursor = mQueryInterface.query(queryUri, projection, null, null, null);
+ try {
+ byte[] signature = null;
+ if (cursor != null && cursor.moveToFirst()) {
+ int signatureCol = 0;
+
+ signature = cursor.getBlob(signatureCol);
+ }
+ return signature;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+}
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/KeychainExternalContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java
new file mode 100644
index 000000000..a4d35f168
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 Vincent Breitmoser <v.breitmoser@mugenguild.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.provider;
+
+
+import android.net.Uri;
+import android.provider.BaseColumns;
+
+import org.sufficientlysecure.keychain.Constants;
+
+
+public class KeychainExternalContract {
+
+ // this is in KeychainExternalContract already, but we want to be double
+ // sure this isn't mixed up with the internal one!
+ public static final String CONTENT_AUTHORITY_EXTERNAL = Constants.PROVIDER_AUTHORITY + ".exported";
+
+ private static final Uri BASE_CONTENT_URI_EXTERNAL = Uri
+ .parse("content://" + CONTENT_AUTHORITY_EXTERNAL);
+
+ public static final String BASE_EMAIL_STATUS = "email_status";
+
+ public static class EmailStatus implements BaseColumns {
+ public static final String EMAIL_ADDRESS = "email_address";
+ public static final String EMAIL_STATUS = "email_status";
+
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI_EXTERNAL.buildUpon()
+ .appendPath(BASE_EMAIL_STATUS).build();
+
+ public static final String CONTENT_TYPE
+ = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.email_status";
+ }
+
+ private KeychainExternalContract() {
+ }
+
+}
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 87b8a3b65..8a5d09d7b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2014-2016 Vincent Breitmoser <v.breitmoser@mugenguild.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
@@ -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 83e2baf9a..a0ebc691d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.provider;
+
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -29,19 +30,12 @@ import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.v4.util.LongSparseArray;
-import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
@@ -51,24 +45,25 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys;
-import org.sufficientlysecure.keychain.remote.AccountSettings;
-import org.sufficientlysecure.keychain.remote.AppSettings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
+import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
+import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.ProgressFixedScaler;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.Utf8Util;
@@ -80,10 +75,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -459,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);
@@ -753,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;
@@ -1444,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);
@@ -1481,191 +1482,6 @@ public class ProviderHelper {
return mContentResolver.insert(UpdatedKeys.CONTENT_URI, values);
}
- public ArrayList<String> getRegisteredApiApps() {
- Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
-
- ArrayList<String> packageNames = new ArrayList<>();
- try {
- if (cursor != null) {
- int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
- if (cursor.moveToFirst()) {
- do {
- packageNames.add(cursor.getString(packageNameCol));
- } while (cursor.moveToNext());
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return packageNames;
- }
-
- private ContentValues contentValueForApiApps(AppSettings appSettings) {
- ContentValues values = new ContentValues();
- values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
- values.put(ApiApps.PACKAGE_CERTIFICATE, appSettings.getPackageCertificate());
- return values;
- }
-
- private ContentValues contentValueForApiAccounts(AccountSettings accSettings) {
- ContentValues values = new ContentValues();
- values.put(KeychainContract.ApiAccounts.ACCOUNT_NAME, accSettings.getAccountName());
- values.put(KeychainContract.ApiAccounts.KEY_ID, accSettings.getKeyId());
-
- // DEPRECATED and thus hardcoded
- values.put(KeychainContract.ApiAccounts.COMPRESSION, CompressionAlgorithmTags.ZLIB);
- values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM,
- PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
- values.put(KeychainContract.ApiAccounts.HASH_ALORITHM,
- PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT);
- return values;
- }
-
- public void insertApiApp(AppSettings appSettings) {
- mContentResolver.insert(KeychainContract.ApiApps.CONTENT_URI,
- contentValueForApiApps(appSettings));
- }
-
- public void insertApiAccount(Uri uri, AccountSettings accSettings) {
- mContentResolver.insert(uri, contentValueForApiAccounts(accSettings));
- }
-
- public void updateApiAccount(Uri uri, AccountSettings accSettings) {
- if (mContentResolver.update(uri, contentValueForApiAccounts(accSettings), null,
- null) <= 0) {
- throw new RuntimeException();
- }
- }
-
- /**
- * Must be an uri pointing to an account
- */
- public AppSettings getApiAppSettings(Uri uri) {
- AppSettings settings = null;
-
- Cursor cursor = mContentResolver.query(uri, null, null, null, null);
- try {
- if (cursor != null && cursor.moveToFirst()) {
- settings = new AppSettings();
- settings.setPackageName(cursor.getString(
- cursor.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
- settings.setPackageCertificate(cursor.getBlob(
- cursor.getColumnIndex(KeychainContract.ApiApps.PACKAGE_CERTIFICATE)));
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return settings;
- }
-
- public AccountSettings getApiAccountSettings(Uri accountUri) {
- AccountSettings settings = null;
-
- Cursor cursor = mContentResolver.query(accountUri, null, null, null, null);
- try {
- if (cursor != null && cursor.moveToFirst()) {
- settings = new AccountSettings();
-
- settings.setAccountName(cursor.getString(
- cursor.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
- settings.setKeyId(cursor.getLong(
- cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return settings;
- }
-
- public Set<Long> getAllKeyIdsForApp(Uri uri) {
- Set<Long> keyIds = new HashSet<>();
-
- Cursor cursor = mContentResolver.query(uri, null, null, null, null);
- try {
- if (cursor != null) {
- int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID);
- while (cursor.moveToNext()) {
- keyIds.add(cursor.getLong(keyIdColumn));
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return keyIds;
- }
-
- public HashSet<Long> getAllowedKeyIdsForApp(Uri uri) {
- HashSet<Long> keyIds = new HashSet<>();
-
- Cursor cursor = mContentResolver.query(uri, null, null, null, null);
- try {
- if (cursor != null) {
- int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAllowedKeys.KEY_ID);
- while (cursor.moveToNext()) {
- keyIds.add(cursor.getLong(keyIdColumn));
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return keyIds;
- }
-
- public void saveAllowedKeyIdsForApp(Uri uri, Set<Long> allowedKeyIds)
- throws RemoteException, OperationApplicationException {
- // wipe whole table of allowed keys for this account
- mContentResolver.delete(uri, null, null);
-
- // re-insert allowed key ids
- for (Long keyId : allowedKeyIds) {
- ContentValues values = new ContentValues();
- values.put(ApiAllowedKeys.KEY_ID, keyId);
- mContentResolver.insert(uri, values);
- }
- }
-
- public void addAllowedKeyIdForApp(Uri uri, long allowedKeyId) {
- ContentValues values = new ContentValues();
- values.put(ApiAllowedKeys.KEY_ID, allowedKeyId);
- mContentResolver.insert(uri, values);
- }
-
- public byte[] getApiAppCertificate(String packageName) {
- Uri queryUri = ApiApps.buildByPackageNameUri(packageName);
-
- String[] projection = new String[]{ApiApps.PACKAGE_CERTIFICATE};
-
- Cursor cursor = mContentResolver.query(queryUri, projection, null, null, null);
- try {
- byte[] signature = null;
- if (cursor != null && cursor.moveToFirst()) {
- int signatureCol = 0;
-
- signature = cursor.getBlob(signatureCol);
- }
- return signature;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
public ContentResolver getContentResolver() {
return mContentResolver;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/SimpleContentResolverInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/SimpleContentResolverInterface.java
new file mode 100644
index 000000000..0e4d76aa4
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/SimpleContentResolverInterface.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Vincent Breitmoser <v.breitmoser@mugenguild.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.provider;
+
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/** This interface contains the principal methods for database access
+ * from {#android.content.ContentResolver}. It is used to allow substitution
+ * of a ContentResolver in DAOs.
+ *
+ * @see ApiDataAccessObject
+ */
+public interface SimpleContentResolverInterface {
+ Cursor query(Uri contentUri, String[] projection, String selection, String[] selectionArgs, String sortOrder);
+
+ Uri insert(Uri contentUri, ContentValues values);
+
+ int update(Uri contentUri, ContentValues values, String where, String[] selectionArgs);
+
+ int delete(Uri contentUri, String where, String[] selectionArgs);
+}
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 7c05edb71..4107167b5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java
@@ -51,10 +51,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: {
@@ -66,7 +66,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/ApiPermissionHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPermissionHelper.java
index 7edd8b2b0..47ecdb21f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPermissionHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPermissionHelper.java
@@ -18,6 +18,10 @@
package org.sufficientlysecure.keychain.remote;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
@@ -33,15 +37,10 @@ import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-
/**
* Abstract service class for remote APIs that handle app registration and user input.
@@ -49,13 +48,13 @@ import java.util.Arrays;
public class ApiPermissionHelper {
private final Context mContext;
- private final ProviderHelper mProviderHelper;
+ private final ApiDataAccessObject mApiDao;
private PackageManager mPackageManager;
- public ApiPermissionHelper(Context context) {
+ public ApiPermissionHelper(Context context, ApiDataAccessObject apiDao) {
mContext = context;
mPackageManager = context.getPackageManager();
- mProviderHelper = new ProviderHelper(context);
+ mApiDao = apiDao;
}
public static class WrongPackageCertificateException extends Exception {
@@ -66,14 +65,24 @@ public class ApiPermissionHelper {
}
}
+ /** Returns true iff the caller is allowed, or false on any type of problem.
+ * This method should only be used in cases where error handling is dealt with separately.
+ */
+ protected boolean isAllowedIgnoreErrors() {
+ try {
+ return isCallerAllowed();
+ } catch (WrongPackageCertificateException e) {
+ return false;
+ }
+ }
+
/**
* Checks if caller is allowed to access the API
*
* @return null if caller is allowed, or a Bundle with a PendingIntent
*/
- protected Intent isAllowed(Intent data) {
+ protected Intent isAllowedOrReturnIntent(Intent data) {
ApiPendingIntentFactory piFactory = new ApiPendingIntentFactory(mContext);
-
try {
if (isCallerAllowed()) {
return null;
@@ -168,7 +177,7 @@ public class ApiPermissionHelper {
Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName);
- return mProviderHelper.getApiAccountSettings(uri); // can be null!
+ return mApiDao.getApiAccountSettings(uri); // can be null!
}
@Deprecated
@@ -224,35 +233,29 @@ public class ApiPermissionHelper {
private boolean isPackageAllowed(String packageName) throws WrongPackageCertificateException {
Log.d(Constants.TAG, "isPackageAllowed packageName: " + packageName);
- ArrayList<String> allowedPkgs = mProviderHelper.getRegisteredApiApps();
- Log.d(Constants.TAG, "allowed: " + allowedPkgs);
+ byte[] storedPackageCert = mApiDao.getApiAppCertificate(packageName);
- // check if package is allowed to use our service
- if (allowedPkgs.contains(packageName)) {
- Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName);
+ boolean isKnownPackage = storedPackageCert != null;
+ if (!isKnownPackage) {
+ Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName);
+ return false;
+ }
+ Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName);
- // check package signature
- byte[] currentCert;
- try {
- currentCert = getPackageCertificate(packageName);
- } catch (NameNotFoundException e) {
- throw new WrongPackageCertificateException(e.getMessage());
- }
+ byte[] currentPackageCert;
+ try {
+ currentPackageCert = getPackageCertificate(packageName);
+ } catch (NameNotFoundException e) {
+ throw new WrongPackageCertificateException(e.getMessage());
+ }
- byte[] storedCert = mProviderHelper.getApiAppCertificate(packageName);
- if (Arrays.equals(currentCert, storedCert)) {
- Log.d(Constants.TAG,
- "Package certificate is correct! (equals certificate from database)");
- return true;
- } else {
- throw new WrongPackageCertificateException(
- "PACKAGE NOT ALLOWED! Certificate wrong! (Certificate not " +
- "equals certificate from database)");
- }
+ boolean packageCertMatchesStored = Arrays.equals(currentPackageCert, storedPackageCert);
+ if (packageCertMatchesStored) {
+ Log.d(Constants.TAG,"Package certificate matches expected.");
+ return true;
}
- Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName);
- return false;
+ throw new WrongPackageCertificateException("PACKAGE NOT ALLOWED DUE TO CERTIFICATE MISMATCH!");
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java
new file mode 100644
index 000000000..455857f00
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2016 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * 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.remote;
+
+
+import java.security.AccessControlException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.Binder;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.provider.KeychainExternalContract;
+import org.sufficientlysecure.keychain.provider.KeychainExternalContract.EmailStatus;
+import org.sufficientlysecure.keychain.provider.SimpleContentResolverInterface;
+import org.sufficientlysecure.keychain.util.Log;
+
+
+public class KeychainExternalProvider extends ContentProvider implements SimpleContentResolverInterface {
+ private static final int EMAIL_STATUS = 101;
+ private static final int API_APPS = 301;
+ private static final int API_APPS_BY_PACKAGE_NAME = 302;
+
+
+ private UriMatcher mUriMatcher;
+ private ApiPermissionHelper mApiPermissionHelper;
+
+
+ /**
+ * Build and return a {@link UriMatcher} that catches all {@link Uri} variations supported by
+ * this {@link ContentProvider}.
+ */
+ protected UriMatcher buildUriMatcher() {
+ final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ String authority = KeychainExternalContract.CONTENT_AUTHORITY_EXTERNAL;
+
+ /**
+ * list email_status
+ *
+ * <pre>
+ * email_status/
+ * </pre>
+ */
+ matcher.addURI(authority, KeychainExternalContract.BASE_EMAIL_STATUS, EMAIL_STATUS);
+
+ matcher.addURI(KeychainContract.CONTENT_AUTHORITY, KeychainContract.BASE_API_APPS, API_APPS);
+ matcher.addURI(KeychainContract.CONTENT_AUTHORITY, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME);
+
+ return matcher;
+ }
+
+ private KeychainDatabase mKeychainDatabase;
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onCreate() {
+ mUriMatcher = buildUriMatcher();
+ mApiPermissionHelper = new ApiPermissionHelper(getContext(), new ApiDataAccessObject(this));
+ return true;
+ }
+
+ public KeychainDatabase getDb() {
+ if(mKeychainDatabase == null)
+ mKeychainDatabase = new KeychainDatabase(getContext());
+ return mKeychainDatabase;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getType(@NonNull Uri uri) {
+ final int match = mUriMatcher.match(uri);
+ switch (match) {
+ case EMAIL_STATUS:
+ return EmailStatus.CONTENT_TYPE;
+
+ case API_APPS:
+ return ApiApps.CONTENT_TYPE;
+
+ case API_APPS_BY_PACKAGE_NAME:
+ return ApiApps.CONTENT_ITEM_TYPE;
+
+ default:
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
+
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+
+ int match = mUriMatcher.match(uri);
+
+ String groupBy = null;
+
+ switch (match) {
+ case EMAIL_STATUS: {
+ boolean callerIsAllowed = mApiPermissionHelper.isAllowedIgnoreErrors();
+ if (!callerIsAllowed) {
+ throw new AccessControlException("An application must register before use of KeychainExternalProvider!");
+ }
+
+ HashMap<String, String> projectionMap = new HashMap<>();
+ projectionMap.put(EmailStatus._ID, "email AS _id");
+ projectionMap.put(EmailStatus.EMAIL_ADDRESS,
+ Tables.USER_PACKETS + "." + UserPackets.USER_ID + " AS " + EmailStatus.EMAIL_ADDRESS);
+ // we take the minimum (>0) here, where "1" is "verified by known secret key", "2" is "self-certified"
+ projectionMap.put(EmailStatus.EMAIL_STATUS, "CASE ( MIN (" + Certs.VERIFIED + " ) ) "
+ // remap to keep this provider contract independent from our internal representation
+ + " WHEN " + Certs.VERIFIED_SELF + " THEN 1"
+ + " WHEN " + Certs.VERIFIED_SECRET + " THEN 2"
+ + " END AS " + EmailStatus.EMAIL_STATUS);
+ qb.setProjectionMap(projectionMap);
+
+ if (projection == null) {
+ throw new IllegalArgumentException("Please provide a projection!");
+ }
+
+ qb.setTables(
+ Tables.USER_PACKETS
+ + " INNER JOIN " + Tables.CERTS + " ON ("
+ + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = "
+ + Tables.CERTS + "." + Certs.MASTER_KEY_ID
+ + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = "
+ + Tables.CERTS + "." + Certs.RANK
+ // verified == 0 has no self-cert, which is basically an error case. never return that!
+ + " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0"
+ + ")"
+ );
+ qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.USER_ID + " IS NOT NULL");
+ // in case there are multiple verifying certificates
+ groupBy = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + ", "
+ + Tables.USER_PACKETS + "." + UserPackets.USER_ID;
+
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = EmailStatus.EMAIL_ADDRESS + " ASC, " + EmailStatus.EMAIL_STATUS + " DESC";
+ }
+
+ // uri to watch is all /key_rings/
+ uri = KeyRings.CONTENT_URI;
+
+ boolean gotCondition = false;
+ String emailWhere = "";
+ // JAVA ♥
+ for (int i = 0; i < selectionArgs.length; ++i) {
+ if (selectionArgs[i].length() == 0) {
+ continue;
+ }
+ if (i != 0) {
+ emailWhere += " OR ";
+ }
+ emailWhere += UserPackets.USER_ID + " LIKE ";
+ // match '*<email>', so it has to be at the *end* of the user id
+ emailWhere += DatabaseUtils.sqlEscapeString("%<" + selectionArgs[i] + ">");
+ gotCondition = true;
+ }
+
+ if (gotCondition) {
+ qb.appendWhere(" AND (" + emailWhere + ")");
+ } else {
+ // TODO better way to do this?
+ Log.e(Constants.TAG, "Malformed find by email query!");
+ qb.appendWhere(" AND 0");
+ }
+
+ break;
+ }
+
+ case API_APPS_BY_PACKAGE_NAME: {
+ String requestedPackageName = uri.getLastPathSegment();
+ checkIfPackageBelongsToCaller(getContext(), requestedPackageName);
+
+ qb.setTables(Tables.API_APPS);
+ qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(requestedPackageName);
+
+ break;
+ }
+
+ default: {
+ throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
+ }
+
+ }
+
+ // If no sort order is specified use the default
+ String orderBy;
+ if (TextUtils.isEmpty(sortOrder)) {
+ orderBy = null;
+ } else {
+ orderBy = sortOrder;
+ }
+
+ SQLiteDatabase db = getDb().getReadableDatabase();
+
+ Cursor cursor = qb.query(db, projection, selection, null, groupBy, null, orderBy);
+ if (cursor != null) {
+ // Tell the cursor what uri to watch, so it knows when its source data changes
+ cursor.setNotificationUri(getContext().getContentResolver(), uri);
+ }
+
+ Log.d(Constants.TAG,
+ "Query: " + qb.buildQuery(projection, selection, null, null, orderBy, null));
+
+ return cursor;
+ }
+
+ private void checkIfPackageBelongsToCaller(Context context, String requestedPackageName) {
+ int callerUid = Binder.getCallingUid();
+ String[] callerPackageNames = context.getPackageManager().getPackagesForUid(callerUid);
+ if (callerPackageNames == null) {
+ throw new IllegalStateException("Failed to retrieve caller package name, this is an error!");
+ }
+
+ boolean packageBelongsToCaller = false;
+ for (String p : callerPackageNames) {
+ if (p.equals(requestedPackageName)) {
+ packageBelongsToCaller = true;
+ break;
+ }
+ }
+ if (!packageBelongsToCaller) {
+ throw new SecurityException("ExternalProvider may only check status of caller package!");
+ }
+ }
+
+ @Override
+ public Uri insert(@NonNull Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, String additionalSelection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+}
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 a5dc2a03c..88cd066a2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2016 Vincent Breitmoser <look@my.amazin.horse>
*
* 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
@@ -17,11 +18,24 @@
package org.sufficientlysecure.keychain.remote;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+
import android.app.PendingIntent;
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;
@@ -43,12 +57,15 @@ import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.KeyRing.UserId;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
+import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -61,18 +78,12 @@ import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-
public class OpenPgpService extends Service {
- static final String[] EMAIL_SEARCH_PROJECTION = new String[]{
+ public static final List<Integer> SUPPORTED_VERSIONS =
+ Collections.unmodifiableList(Arrays.asList(3, 4, 5, 6, 7, 8, 9, 10, 11));
+
+ static final String[] KEY_SEARCH_PROJECTION = new String[]{
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
KeyRings.IS_EXPIRED,
@@ -80,35 +91,50 @@ public class OpenPgpService extends Service {
};
// do not pre-select revoked or expired keys
- static final String EMAIL_SEARCH_WHERE = Tables.KEYS + "." + KeychainContract.KeyRings.IS_REVOKED
+ static final String KEY_SEARCH_WHERE = Tables.KEYS + "." + KeychainContract.KeyRings.IS_REVOKED
+ " = 0 AND " + KeychainContract.KeyRings.IS_EXPIRED + " = 0";
private ApiPermissionHelper mApiPermissionHelper;
private ProviderHelper mProviderHelper;
+ private ApiDataAccessObject mApiDao;
@Override
public void onCreate() {
super.onCreate();
- mApiPermissionHelper = new ApiPermissionHelper(this);
+ mApiPermissionHelper = new ApiPermissionHelper(this, new ApiDataAccessObject(this));
mProviderHelper = new ProviderHelper(this);
+ mApiDao = new ApiDataAccessObject(this);
}
- /**
- * Search database for key ids based on emails.
- */
- private Intent returnKeyIdsFromEmails(Intent data, String[] encryptionUserIds) {
+ private static class KeyIdResult {
+ final Intent mResultIntent;
+ final HashSet<Long> mKeyIds;
+
+ KeyIdResult(Intent resultIntent) {
+ mResultIntent = resultIntent;
+ mKeyIds = null;
+ }
+ KeyIdResult(HashSet<Long> keyIds) {
+ mResultIntent = null;
+ mKeyIds = keyIds;
+ }
+ }
+
+ private KeyIdResult returnKeyIdsFromEmails(Intent data, String[] encryptionUserIds, boolean isOpportunistic) {
boolean noUserIdsCheck = (encryptionUserIds == null || encryptionUserIds.length == 0);
boolean missingUserIdsCheck = false;
boolean duplicateUserIdsCheck = false;
- ArrayList<Long> keyIds = new ArrayList<>();
+ HashSet<Long> keyIds = new HashSet<>();
ArrayList<String> missingEmails = new ArrayList<>();
ArrayList<String> duplicateEmails = new ArrayList<>();
if (!noUserIdsCheck) {
- for (String email : encryptionUserIds) {
+ for (String rawUserId : encryptionUserIds) {
+ UserId userId = KeyRing.splitUserId(rawUserId);
+ String email = userId.email != null ? userId.email : rawUserId;
// try to find the key for this specific email
Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
- Cursor cursor = getContentResolver().query(uri, EMAIL_SEARCH_PROJECTION, EMAIL_SEARCH_WHERE, null, null);
+ Cursor cursor = getContentResolver().query(uri, KEY_SEARCH_PROJECTION, KEY_SEARCH_WHERE, null, null);
try {
// result should be one entry containing the key id
if (cursor != null && cursor.moveToFirst()) {
@@ -137,15 +163,17 @@ public class OpenPgpService extends Service {
}
}
- // convert ArrayList<Long> to long[]
- long[] keyIdsArray = new long[keyIds.size()];
- for (int i = 0; i < keyIdsArray.length; i++) {
- keyIdsArray[i] = keyIds.get(i);
+ 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) {
- // allow the user to verify pub key selection
-
+ // convert ArrayList<Long> to long[]
+ long[] keyIdsArray = getUnboxedLongArray(keyIds);
ApiPendingIntentFactory piFactory = new ApiPendingIntentFactory(getBaseContext());
PendingIntent pi = piFactory.createSelectPublicKeyPendingIntent(data, keyIdsArray,
missingEmails, duplicateEmails, noUserIdsCheck);
@@ -154,19 +182,15 @@ public class OpenPgpService extends Service {
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
- return result;
- } else {
- // everything was easy, we have exactly one key for every email
-
- if (keyIdsArray.length == 0) {
- Log.e(Constants.TAG, "keyIdsArray.length == 0, should never happen!");
- }
+ return new KeyIdResult(result);
+ }
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_KEY_IDS, keyIdsArray);
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
- return result;
+ // 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,
@@ -280,20 +304,31 @@ public class OpenPgpService extends Service {
compressionId = PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED;
}
- // first try to get key ids from non-ambiguous key id extra
- long[] keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
- if (keyIds == null) {
+ long[] keyIds;
+ {
+ HashSet<Long> encryptKeyIds = new HashSet<>();
+
// get key ids based on given user ids
- String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS);
- // give params through to activity...
- Intent result = returnKeyIdsFromEmails(data, userIds);
+ if (data.hasExtra(OpenPgpApi.EXTRA_USER_IDS)) {
+ String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS);
+ boolean isOpportunistic = data.getBooleanExtra(OpenPgpApi.EXTRA_OPPORTUNISTIC_ENCRYPTION, false);
+ // give params through to activity...
+ KeyIdResult result = returnKeyIdsFromEmails(data, userIds, isOpportunistic);
+
+ if (result.mResultIntent != null) {
+ return result.mResultIntent;
+ }
+ encryptKeyIds.addAll(result.mKeyIds);
+ }
- if (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0) == OpenPgpApi.RESULT_CODE_SUCCESS) {
- keyIds = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS);
- } else {
- // if not success -> result contains a PendingIntent for user interaction
- return result;
+ // add key ids from non-ambiguous key id extra
+ if (data.hasExtra(OpenPgpApi.EXTRA_KEY_IDS)) {
+ for (long keyId : data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS)) {
+ encryptKeyIds.add(keyId);
+ }
}
+
+ keyIds = getUnboxedLongArray(encryptKeyIds);
}
// TODO this is not correct!
@@ -305,8 +340,7 @@ public class OpenPgpService extends Service {
.setVersionHeader(null)
.setCompressionAlgorithm(compressionId)
.setSymmetricEncryptionAlgorithm(PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT)
- .setEncryptionMasterKeyIds(keyIds)
- .setFailOnMissingEncryptionKeyIds(true);
+ .setEncryptionMasterKeyIds(keyIds);
if (sign) {
@@ -405,11 +439,11 @@ public class OpenPgpService extends Service {
}
String currentPkg = mApiPermissionHelper.getCurrentCallingPackage();
- HashSet<Long> allowedKeyIds = mProviderHelper.getAllowedKeyIdsForApp(
+ HashSet<Long> allowedKeyIds = mApiDao.getAllowedKeyIdsForApp(
KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg));
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 7) {
- allowedKeyIds.addAll(mProviderHelper.getAllKeyIdsForApp(
+ allowedKeyIds.addAll(mApiDao.getAllKeyIdsForApp(
ApiAccounts.buildBaseUri(currentPkg)));
}
@@ -422,6 +456,15 @@ public class OpenPgpService extends Service {
cryptoInput.mPassphrase =
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
+ if (data.hasExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT_WRAPPER)) {
+ // this is wrapped in a Bundle to avoid ClassLoader problems
+ 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);
@@ -582,7 +625,8 @@ public class OpenPgpService extends Service {
try {
// try to find key, throws NotFoundException if not in db!
CanonicalizedPublicKeyRing keyRing =
- mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId);
+ mProviderHelper.getCanonicalizedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(masterKeyId));
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
@@ -669,7 +713,21 @@ public class OpenPgpService extends Service {
} else {
// get key ids based on given user ids
String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS);
- return returnKeyIdsFromEmails(data, userIds);
+ KeyIdResult keyResult = returnKeyIdsFromEmails(data, userIds, false);
+ if (keyResult.mResultIntent != null) {
+ return keyResult.mResultIntent;
+ }
+
+ if (keyResult.mKeyIds == null) {
+ throw new AssertionError("one of requiredUserInteraction and keyIds must be non-null, this is a bug!");
+ }
+
+ long[] keyIds = getUnboxedLongArray(keyResult.mKeyIds);
+
+ Intent resultIntent = new Intent();
+ resultIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
+ resultIntent.putExtra(OpenPgpApi.RESULT_KEY_IDS, keyIds);
+ return resultIntent;
}
}
@@ -716,6 +774,26 @@ public class OpenPgpService extends Service {
}
}
+ @NonNull
+ private static long[] getUnboxedLongArray(@NonNull Collection<Long> arrayList) {
+ long[] result = new long[arrayList.size()];
+ int i = 0;
+ for (Long e : arrayList) {
+ result[i++] = e;
+ }
+ return result;
+ }
+
+ private Intent checkPermissionImpl(@NonNull Intent data) {
+ Intent permissionIntent = mApiPermissionHelper.isAllowedOrReturnIntent(data);
+ if (permissionIntent != null) {
+ return permissionIntent;
+ }
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
+ return result;
+ }
+
private Intent getSignKeyMasterId(Intent data) {
// NOTE: Accounts are deprecated on API version >= 7
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 7) {
@@ -765,20 +843,19 @@ public class OpenPgpService extends Service {
// version code is required and needs to correspond to version code of service!
// History of versions in openpgp-api's CHANGELOG.md
- List<Integer> supportedVersions = Arrays.asList(3, 4, 5, 6, 7, 8, 9, 10);
- if (!supportedVersions.contains(data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1))) {
+ if (!SUPPORTED_VERSIONS.contains(data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1))) {
Intent result = new Intent();
OpenPgpError error = new OpenPgpError
(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!\n"
+ "used API version: " + data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) + "\n"
- + "supported API versions: " + supportedVersions);
+ + "supported API versions: " + SUPPORTED_VERSIONS);
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
}
// check if caller is allowed to access OpenKeychain
- Intent result = mApiPermissionHelper.isAllowed(data);
+ Intent result = mApiPermissionHelper.isAllowedOrReturnIntent(data);
if (result != null) {
return result;
}
@@ -845,6 +922,9 @@ public class OpenPgpService extends Service {
String action = data.getAction();
switch (action) {
+ case OpenPgpApi.ACTION_CHECK_PERMISSION: {
+ return checkPermissionImpl(data);
+ }
case OpenPgpApi.ACTION_CLEARTEXT_SIGN: {
return signImpl(data, inputStream, outputStream, true);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
index e19757d65..27700b5e6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
@@ -29,7 +29,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.util.Log;
@@ -98,7 +98,7 @@ public class AccountSettingsActivity extends BaseActivity {
}
private void loadData(Uri accountUri) {
- AccountSettings settings = new ProviderHelper(this).getApiAccountSettings(accountUri);
+ AccountSettings settings = new ApiDataAccessObject(this).getApiAccountSettings(accountUri);
mAccountSettingsFragment.setAccSettings(settings);
}
@@ -110,7 +110,7 @@ public class AccountSettingsActivity extends BaseActivity {
}
private void save() {
- new ProviderHelper(this).updateApiAccount(mAccountUri, mAccountSettingsFragment.getAccSettings());
+ new ApiDataAccessObject(this).updateApiAccount(mAccountUri, mAccountSettingsFragment.getAccSettings());
SingletonResult result = new SingletonResult(
SingletonResult.RESULT_OK, LogType.MSG_ACC_SAVED);
Intent intent = new Intent();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
index 3b5fdfd8a..249c0c208 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
@@ -37,8 +37,8 @@ import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.dialog.AdvancedAppSettingsDialogFragment;
@@ -182,7 +182,7 @@ public class AppSettingsActivity extends BaseActivity {
}
private void loadData(Bundle savedInstanceState, Uri appUri) {
- mAppSettings = new ProviderHelper(this).getApiAppSettings(appUri);
+ mAppSettings = new ApiDataAccessObject(this).getApiAppSettings(appUri);
// get application name and icon from package manager
String appName;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java
index caa173f03..22a03a7c5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java
@@ -36,8 +36,8 @@ import android.widget.ListView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
import org.sufficientlysecure.keychain.ui.adapter.KeySelectableAdapter;
import org.sufficientlysecure.keychain.ui.widget.FixedListView;
@@ -47,7 +47,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
private static final String ARG_DATA_URI = "uri";
private KeySelectableAdapter mAdapter;
- private ProviderHelper mProviderHelper;
+ private ApiDataAccessObject mApiDao;
private Uri mDataUri;
@@ -69,7 +69,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mProviderHelper = new ProviderHelper(getActivity());
+ mApiDao = new ApiDataAccessObject(getActivity());
}
@Override
@@ -107,7 +107,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
// application this would come from a resource.
setEmptyText(getString(R.string.list_empty));
- Set<Long> checked = mProviderHelper.getAllKeyIdsForApp(mDataUri);
+ Set<Long> checked = mApiDao.getAllKeyIdsForApp(mDataUri);
mAdapter = new KeySelectableAdapter(getActivity(), null, 0, checked);
setListAdapter(mAdapter);
getListView().setOnItemClickListener(mAdapter);
@@ -141,7 +141,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
public void saveAllowedKeys() {
try {
- mProviderHelper.saveAllowedKeyIdsForApp(mDataUri, getSelectedMasterKeyIds());
+ mApiDao.saveAllowedKeyIdsForApp(mDataUri, getSelectedMasterKeyIds());
} catch (RemoteException | OperationApplicationException e) {
Log.e(Constants.TAG, "Problem saving allowed key ids!", e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteCreateAccountActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteCreateAccountActivity.java
index adc1f5abf..a4a0b242c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteCreateAccountActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteCreateAccountActivity.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.remote.ui;
+
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -25,8 +26,8 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.util.Notify;
@@ -56,7 +57,7 @@ public class RemoteCreateAccountActivity extends BaseActivity {
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
final String accName = extras.getString(EXTRA_ACC_NAME);
- final ProviderHelper providerHelper = new ProviderHelper(this);
+ final ApiDataAccessObject apiDao = new ApiDataAccessObject(this);
mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
R.id.api_account_settings_fragment);
@@ -65,7 +66,7 @@ public class RemoteCreateAccountActivity extends BaseActivity {
// update existing?
Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(packageName, accName);
- AccountSettings settings = providerHelper.getApiAccountSettings(uri);
+ AccountSettings settings = apiDao.getApiAccountSettings(uri);
if (settings == null) {
// create new account
settings = new AccountSettings(accName);
@@ -94,11 +95,11 @@ public class RemoteCreateAccountActivity extends BaseActivity {
if (mUpdateExistingAccount) {
Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(packageName);
Uri accountUri = baseUri.buildUpon().appendEncodedPath(accName).build();
- providerHelper.updateApiAccount(
+ apiDao.updateApiAccount(
accountUri,
mAccSettingsFragment.getAccSettings());
} else {
- providerHelper.insertApiAccount(
+ apiDao.insertApiAccount(
KeychainContract.ApiAccounts.buildBaseUri(packageName),
mAccSettingsFragment.getAccSettings());
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterActivity.java
index d31f9e35a..0ce9e66c9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterActivity.java
@@ -17,13 +17,14 @@
package org.sufficientlysecure.keychain.remote.ui;
+
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.util.Log;
@@ -52,7 +53,7 @@ public class RemoteRegisterActivity extends BaseActivity {
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
Log.d(Constants.TAG, "ACTION_REGISTER packageName: " + packageName);
- final ProviderHelper providerHelper = new ProviderHelper(this);
+ final ApiDataAccessObject apiDao = new ApiDataAccessObject(this);
mAppSettingsHeaderFragment = (AppSettingsHeaderFragment) getSupportFragmentManager().findFragmentById(
R.id.api_app_settings_fragment);
@@ -67,8 +68,7 @@ public class RemoteRegisterActivity extends BaseActivity {
@Override
public void onClick(View v) {
// Allow
-
- providerHelper.insertApiApp(mAppSettingsHeaderFragment.getAppSettings());
+ apiDao.insertApiApp(mAppSettingsHeaderFragment.getAppSettings());
// give data through for new service call
Intent resultData = extras.getParcelable(EXTRA_DATA);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdListFragment.java
index 852e8bb81..73ce5fe47 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdListFragment.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.remote.ui;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -36,9 +37,9 @@ import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
import org.sufficientlysecure.keychain.ui.widget.FixedListView;
import org.sufficientlysecure.keychain.util.Log;
@@ -49,7 +50,7 @@ public class SelectSignKeyIdListFragment extends ListFragmentWorkaround implemen
public static final String ARG_DATA = "data";
private SelectKeyCursorAdapter mAdapter;
- private ProviderHelper mProviderHelper;
+ private ApiDataAccessObject mApiDao;
private Uri mDataUri;
@@ -72,7 +73,7 @@ public class SelectSignKeyIdListFragment extends ListFragmentWorkaround implemen
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mProviderHelper = new ProviderHelper(getActivity());
+ mApiDao = new ApiDataAccessObject(getActivity());
}
@Override
@@ -116,7 +117,7 @@ public class SelectSignKeyIdListFragment extends ListFragmentWorkaround implemen
Uri allowedKeysUri = mDataUri.buildUpon().appendPath(KeychainContract.PATH_ALLOWED_KEYS).build();
Log.d(Constants.TAG, "allowedKeysUri: " + allowedKeysUri);
- mProviderHelper.addAllowedKeyIdForApp(allowedKeysUri, masterKeyId);
+ mApiDao.addAllowedKeyIdForApp(allowedKeysUri, masterKeyId);
resultData.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, masterKeyId);
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..ba36ebdf0
--- /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.BaseSecurityTokenActivity;
+
+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 BaseSecurityTokenActivity.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..30893afb6
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
@@ -0,0 +1,756 @@
+/*
+ * 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/ChangeUnlockParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ChangeUnlockParcel.java
new file mode 100644
index 000000000..974bb2413
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ChangeUnlockParcel.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.util.Passphrase;
+
+public class ChangeUnlockParcel implements Parcelable {
+
+ // the master key id of keyring.
+ public Long mMasterKeyId;
+ // the key fingerprint, for safety.
+ public byte[] mFingerprint;
+ // The new passphrase to use
+ public final Passphrase mNewPassphrase;
+
+ public ChangeUnlockParcel(Passphrase newPassphrase) {
+ mNewPassphrase = newPassphrase;
+ }
+
+ public ChangeUnlockParcel(Long masterKeyId, byte[] fingerprint, Passphrase newPassphrase) {
+ if (newPassphrase == null) {
+ throw new AssertionError("newPassphrase must be non-null. THIS IS A BUG!");
+ }
+
+ mMasterKeyId = masterKeyId;
+ mFingerprint = fingerprint;
+ mNewPassphrase = newPassphrase;
+ }
+
+ public ChangeUnlockParcel(Parcel source) {
+ mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;
+ mFingerprint = source.createByteArray();
+ mNewPassphrase = source.readParcelable(Passphrase.class.getClassLoader());
+ }
+
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeInt(mMasterKeyId == null ? 0 : 1);
+ if (mMasterKeyId != null) {
+ destination.writeLong(mMasterKeyId);
+ }
+ destination.writeByteArray(mFingerprint);
+ destination.writeParcelable(mNewPassphrase, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ChangeUnlockParcel> CREATOR = new Creator<ChangeUnlockParcel>() {
+ public ChangeUnlockParcel createFromParcel(final Parcel source) {
+ return new ChangeUnlockParcel(source);
+ }
+
+ public ChangeUnlockParcel[] newArray(final int size) {
+ return new ChangeUnlockParcel[size];
+ }
+ };
+
+ public String toString() {
+ String out = "mMasterKeyId: " + mMasterKeyId + "\n";
+ out += "passphrase (" + mNewPassphrase + ")";
+
+ return out;
+ }
+
+}
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 cdfe2f1ce..965d15138 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
@@ -143,6 +143,14 @@ public class ContactSyncAdapterService extends Service {
}
public static void requestContactsSync() {
+ // if user has disabled automatic sync, do nothing
+ 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/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
index cf51e3b55..c287f6b38 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
@@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.operations.BackupOperation;
import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation;
import org.sufficientlysecure.keychain.operations.InputDataOperation;
+import org.sufficientlysecure.keychain.operations.ChangeUnlockOperation;
import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
import org.sufficientlysecure.keychain.operations.RevokeOperation;
import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
@@ -116,6 +117,8 @@ public class KeychainService extends Service implements Progressable {
op = new PgpDecryptVerifyOperation(outerThis, new ProviderHelper(outerThis), outerThis);
} else if (inputParcel instanceof SaveKeyringParcel) {
op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
+ } else if (inputParcel instanceof ChangeUnlockParcel) {
+ op = new ChangeUnlockOperation(outerThis, new ProviderHelper(outerThis), outerThis);
} else if (inputParcel instanceof RevokeKeyringParcel) {
op = new RevokeOperation(outerThis, new ProviderHelper(outerThis), outerThis);
} else if (inputParcel instanceof CertifyActionsParcel) {
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..db6bbcbdb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -49,8 +49,6 @@ public class SaveKeyringParcel implements Parcelable {
// the key fingerprint, for safety. MUST be null for a new key.
public byte[] mFingerprint;
- public ChangeUnlockParcel mNewUnlock;
-
public ArrayList<String> mAddUserIds;
public ArrayList<WrappedUserAttribute> mAddUserAttribute;
public ArrayList<SubkeyAdd> mAddSubKeys;
@@ -70,6 +68,9 @@ public class SaveKeyringParcel implements Parcelable {
private boolean mUploadAtomic;
private String mKeyserver;
+ // private because we have to set other details like key id
+ private ChangeUnlockParcel mNewUnlock;
+
public SaveKeyringParcel() {
reset();
}
@@ -102,6 +103,18 @@ public class SaveKeyringParcel implements Parcelable {
mKeyserver = keysever;
}
+ public void setNewUnlock(ChangeUnlockParcel parcel) {
+ mNewUnlock = parcel;
+ }
+
+ public ChangeUnlockParcel getChangeUnlockParcel() {
+ if(mNewUnlock != null) {
+ mNewUnlock.mMasterKeyId = mMasterKeyId;
+ mNewUnlock.mFingerprint = mFingerprint;
+ }
+ return mNewUnlock;
+ }
+
public boolean isUpload() {
return mUpload;
}
@@ -344,64 +357,6 @@ public class SaveKeyringParcel implements Parcelable {
// BRAINPOOL_P256, BRAINPOOL_P384, BRAINPOOL_P512
}
- /** This subclass contains information on how the passphrase should be changed.
- *
- * If no changes are to be made, this class should NOT be used!
- *
- * At this point, there must be *exactly one* non-null value here, which specifies the type
- * of unlocking mechanism to use.
- *
- */
- public static class ChangeUnlockParcel 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!");
- }
- 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
- public int describeContents() {
- return 0;
- }
-
- public static final Creator<ChangeUnlockParcel> CREATOR = new Creator<ChangeUnlockParcel>() {
- public ChangeUnlockParcel createFromParcel(final Parcel source) {
- return new ChangeUnlockParcel(source);
- }
- public ChangeUnlockParcel[] newArray(final int size) {
- return new ChangeUnlockParcel[size];
- }
- };
-
- public String toString() {
- return mNewPassphrase != null
- ? ("passphrase (" + mNewPassphrase + ")")
- : ("pin (" + mNewPin + ")");
- }
-
- }
}
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 55344030a..fb332563d 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;
@@ -43,7 +35,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;
@@ -53,12 +45,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;
@@ -73,6 +62,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 {
@@ -100,7 +97,7 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
String mBackupCode;
private boolean mExecuteBackupOperation;
- private MaskedEditText mCodeEditText;
+ private EditText[] mCodeEditText;
private ToolableViewAnimator mStatusAnimator, mTitleAnimator, mCodeFieldsAnimator;
private Integer mBackStackLevel;
@@ -152,8 +149,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;
@@ -177,11 +179,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();
@@ -195,7 +195,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);
}
@@ -214,14 +214,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();
@@ -257,22 +261,38 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
mExportSecret = args.getBoolean(ARG_EXPORT_SECRET);
mExecuteBackupOperation = args.getBoolean(ARG_EXECUTE_BACKUP_OPERATION, true);
- // 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);
@@ -350,67 +370,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) {
- boolean inInputState = mCurrentState == BackupCodeState.STATE_INPUT
- || mCurrentState == BackupCodeState.STATE_INPUT_ERROR;
- boolean partIsComplete = (backupCode.getText().toString().indexOf(' ') == -1)
- && (backupCode.getText().toString().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(backupCode);
- }
- });
+ }
}
- private void checkIfCodeIsCorrect(EditText backupCode) {
+ private void checkIfCodeIsCorrect() {
if (Constants.DEBUG && mDebugModeAcceptAnyCode) {
switchState(BackupCodeState.STATE_OK, true);
return;
}
- if (backupCode.toString().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);
@@ -421,6 +450,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..b71917368 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import org.sufficientlysecure.keychain.R;
@@ -28,7 +29,7 @@ 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.ui.base.BaseSecurityTokenNfcActivity;
+import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -36,7 +37,7 @@ import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException;
import java.util.ArrayList;
-public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
+public class CreateKeyActivity extends BaseSecurityTokenActivity {
public static final String EXTRA_NAME = "name";
public static final String EXTRA_EMAIL = "email";
@@ -47,9 +48,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 +67,8 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
byte[] mScannedFingerprints;
- byte[] mNfcAid;
- String mNfcUserId;
+ byte[] mSecurityTokenAid;
+ String mSecurityTokenUserId;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -77,7 +78,7 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
// NOTE: ACTION_NDEF_DISCOVERED and not ACTION_TAG_DISCOVERED like in BaseNfcActivity
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
- mTagDispatcher.interceptIntent(getIntent());
+ mNfcTagDispatcher.interceptIntent(getIntent());
setTitle(R.string.title_manage_my_keys);
@@ -107,10 +108,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 +144,32 @@ 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) {
+ // hack from http://stackoverflow.com/a/11253987
+ CreateSecurityTokenWaitFragment.sDisableFragmentAnimations = true;
+ getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ CreateSecurityTokenWaitFragment.sDisableFragmentAnimations = false;
+ }
+
if (containsKeys(mScannedFingerprints)) {
try {
long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mScannedFingerprints);
@@ -169,15 +178,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 {
@@ -252,12 +261,11 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
// do it immediately!
getSupportFragmentManager().executePendingTransactions();
-
}
- 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..227d6fce4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -44,9 +44,9 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
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.UploadKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
@@ -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,18 +289,20 @@ public class CreateKeyFinalFragment extends Fragment {
2048, null, KeyFlags.AUTHENTICATION, 0L));
// use empty passphrase
- saveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase(), null);
+ saveKeyringParcel.setNewUnlock(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)
- : null;
+ if (createKeyActivity.mPassphrase != null) {
+ saveKeyringParcel.setNewUnlock(new ChangeUnlockParcel(createKeyActivity.mPassphrase));
+ } else {
+ saveKeyringParcel.setNewUnlock(null);
+ }
}
String userId = KeyRing.createUserId(
new KeyRing.UserId(createKeyActivity.mName, createKeyActivity.mEmail, null)
@@ -309,6 +322,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..782502741 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
@@ -17,23 +17,36 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.Activity;
+import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.Animation;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
-
+import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
public class CreateSecurityTokenWaitFragment extends Fragment {
+ public static boolean sDisableFragmentAnimations = false;
+
CreateKeyActivity mCreateKeyActivity;
View mBackButton;
@Override
+ public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ if (this.getActivity() instanceof BaseSecurityTokenActivity) {
+ ((BaseSecurityTokenActivity) 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);
@@ -50,9 +63,22 @@ public class CreateSecurityTokenWaitFragment extends Fragment {
}
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
+ public void onAttach(Context context) {
+ super.onAttach(context);
mCreateKeyActivity = (CreateKeyActivity) getActivity();
}
+ /**
+ * hack from http://stackoverflow.com/a/11253987
+ */
+ @Override
+ public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
+ if (sDisableFragmentAnimations) {
+ Animation a = new Animation() {};
+ a.setDuration(0);
+ return a;
+ }
+ return super.onCreateAnimation(transit, enter, nextAnim);
+ }
+
}
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..80fea7b23 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;
@@ -49,8 +50,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
@@ -128,7 +129,7 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.edit_key_fragment, null);
+ View view = inflater.inflate(R.layout.edit_key_fragment, superContainer, false);
mUserIdsList = (ListView) view.findViewById(R.id.edit_key_user_ids);
mSubkeysList = (ListView) view.findViewById(R.id.edit_key_keys);
@@ -338,10 +339,8 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
Bundle data = message.getData();
// cache new returned passphrase!
- mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(
- (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
- null
- );
+ mSaveKeyringParcel.setNewUnlock(new ChangeUnlockParcel(
+ (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE)));
}
}
};
@@ -441,50 +440,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 +556,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 f67c6a724..7d2d30c35 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -131,8 +131,11 @@ public class ImportKeysActivity extends BaseActivity
if (Intent.ACTION_VIEW.equals(action)) {
if (FacebookKeyserver.isFacebookHost(dataUri)) {
action = ACTION_IMPORT_KEY_FROM_FACEBOOK;
- } else if ("http".equals(scheme) || "https".equals(scheme)) {
+ } else if ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)) {
action = ACTION_SEARCH_KEYSERVER_FROM_URL;
+ } else if ("openpgp4fpr".equalsIgnoreCase(scheme)) {
+ action = ACTION_IMPORT_KEY_FROM_KEYSERVER;
+ extras.putString(EXTRA_FINGERPRINT, dataUri.getSchemeSpecificPart());
} else {
// Android's Action when opening file associated to Keychain (see AndroidManifest.xml)
// delegate action to ACTION_IMPORT_KEY
@@ -413,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..13df0b539 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
@@ -40,11 +40,11 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.remote.ui.AppsListFragment;
-import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenNfcActivity;
+import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
import org.sufficientlysecure.keychain.util.FabContainer;
import org.sufficientlysecure.keychain.util.Preferences;
-public class MainActivity extends BaseSecurityTokenNfcActivity implements FabContainer, OnBackStackChangedListener {
+public class MainActivity extends BaseSecurityTokenActivity implements FabContainer, OnBackStackChangedListener {
static final int ID_KEYS = 1;
static final int ID_ENCRYPT_DECRYPT = 2;
@@ -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/RedirectImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RedirectImportKeysActivity.java
new file mode 100644
index 000000000..5cb680a57
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RedirectImportKeysActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
+
+public class RedirectImportKeysActivity extends BaseActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ startQrCodeCaptureActivity();
+ }
+
+ private void startQrCodeCaptureActivity() {
+ final Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class);
+ scanQrCode.setAction(ImportKeysProxyActivity.ACTION_QR_CODE_API);
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.redirect_import_key_title)
+ .setMessage(R.string.redirect_import_key_message)
+ .setPositiveButton(R.string.redirect_import_key_yes, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // directly scan with OpenKeychain
+ startActivity(scanQrCode);
+ finish();
+ }
+ })
+ .setNegativeButton(R.string.redirect_import_key_no, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // close window
+ finish();
+ }
+ })
+ .show();
+ }
+} \ No newline at end of file
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..4d07025e6 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.base.BaseSecurityTokenActivity;
+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;
@@ -55,7 +58,7 @@ import nordpol.android.NfcGuideView;
* NFC devices.
* For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf
*/
-public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity {
+public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
public static final String EXTRA_REQUIRED_INPUT = "required_input";
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
@@ -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..ca4a33980 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;
@@ -80,11 +81,11 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.ViewKeyFragment.PostponeType;
-import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenNfcActivity;
+import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
@@ -102,7 +103,7 @@ import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
-public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
+public class ViewKeyActivity extends BaseSecurityTokenActivity implements
LoaderManager.LoaderCallbacks<Cursor>,
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
@@ -129,8 +130,8 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
private String mKeyserver;
private ArrayList<ParcelableKeyRing> mKeyList;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
- private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mEditOpHelper;
- private SaveKeyringParcel mSaveKeyringParcel;
+ private CryptoOperationHelper<ChangeUnlockParcel, EditKeyResult> mEditOpHelper;
+ private ChangeUnlockParcel mChangeUnlockParcel;
private TextView mStatusText;
private ImageView mStatusImage;
@@ -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
@@ -428,13 +429,11 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
}
private void changePassword() {
- mSaveKeyringParcel = new SaveKeyringParcel(mMasterKeyId, mFingerprint);
-
- CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> editKeyCallback
- = new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() {
+ CryptoOperationHelper.Callback<ChangeUnlockParcel, EditKeyResult> editKeyCallback
+ = new CryptoOperationHelper.Callback<ChangeUnlockParcel, EditKeyResult>() {
@Override
- public SaveKeyringParcel createOperationInput() {
- return mSaveKeyringParcel;
+ public ChangeUnlockParcel createOperationInput() {
+ return mChangeUnlockParcel;
}
@Override
@@ -468,9 +467,10 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
Bundle data = message.getData();
// use new passphrase!
- mSaveKeyringParcel.mNewUnlock = new SaveKeyringParcel.ChangeUnlockParcel(
- (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
- null
+ mChangeUnlockParcel = new ChangeUnlockParcel(
+ mMasterKeyId,
+ mFingerprint,
+ (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/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
index 0201318e8..df24e9877 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -141,6 +141,7 @@ public class ImportKeysListLoader
OperationResult.OperationLog log = new OperationResult.OperationLog();
log.add(OperationResult.LogType.MSG_GET_NO_VALID_KEYS, 0);
GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_ERROR_NO_VALID_KEYS, log);
+ mData.clear();
mEntryListWrapper = new AsyncTaskResultWrapper<>(mData, getKeyResult);
}
}
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..78aaecab3 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
@@ -66,6 +66,7 @@ public class KeyAdapter extends CursorAdapter {
KeyRings.HAS_DUPLICATE_USER_ID,
KeyRings.FINGERPRINT,
KeyRings.CREATION,
+ KeyRings.HAS_ENCRYPT
};
public static final int INDEX_MASTER_KEY_ID = 1;
@@ -77,6 +78,7 @@ public class KeyAdapter extends CursorAdapter {
public static final int INDEX_HAS_DUPLICATE_USER_ID = 7;
public static final int INDEX_FINGERPRINT = 8;
public static final int INDEX_CREATION = 9;
+ public static final int INDEX_HAS_ENCRYPT = 10;
public KeyAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
@@ -289,6 +291,7 @@ public class KeyAdapter extends CursorAdapter {
public final KeyRing.UserId mUserId;
public final long mKeyId;
public final boolean mHasDuplicate;
+ public final boolean mHasEncrypt;
public final Date mCreation;
public final String mFingerprint;
public final boolean mIsSecret, mIsRevoked, mIsExpired, mIsVerified;
@@ -299,6 +302,7 @@ public class KeyAdapter extends CursorAdapter {
mUserIdFull = userId;
mKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
mHasDuplicate = cursor.getLong(INDEX_HAS_DUPLICATE_USER_ID) > 0;
+ mHasEncrypt = cursor.getInt(INDEX_HAS_ENCRYPT) != 0;
mCreation = new Date(cursor.getLong(INDEX_CREATION) * 1000);
mFingerprint = KeyFormattingUtils.convertFingerprintToHex(
cursor.getBlob(INDEX_FINGERPRINT));
@@ -315,6 +319,7 @@ public class KeyAdapter extends CursorAdapter {
mUserIdFull = userId;
mKeyId = ring.getMasterKeyId();
mHasDuplicate = false;
+ mHasEncrypt = key.getKeyRing().getEncryptIds().size() > 0;
mCreation = key.getCreationTime();
mFingerprint = KeyFormattingUtils.convertFingerprintToHex(
ring.getFingerprint());
@@ -333,14 +338,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/BaseSecurityTokenActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java
new file mode 100644
index 000000000..680613596
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 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
+ * 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.base;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+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.os.AsyncTask;
+import android.os.Bundle;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+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.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;
+import org.sufficientlysecure.keychain.ui.dialog.FidesmoInstallDialog;
+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.Log;
+import org.sufficientlysecure.keychain.util.Passphrase;
+
+import java.io.IOException;
+
+import nordpol.android.OnDiscoveredTagListener;
+import nordpol.android.TagDispatcher;
+
+public abstract class BaseSecurityTokenActivity 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";
+
+ private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android";
+
+ protected SecurityTokenHelper mSecurityTokenHelper = SecurityTokenHelper.getInstance();
+ protected TagDispatcher mNfcTagDispatcher;
+ protected UsbConnectionDispatcher mUsbDispatcher;
+ private boolean mTagHandlingEnabled;
+
+ private byte[] mSecurityTokenFingerprints;
+ private String mSecurityTokenUserId;
+ private byte[] mSecurityTokenAid;
+
+ /**
+ * Override to change UI before SecurityToken handling (UI thread)
+ */
+ protected void onSecurityTokenPreExecute() {
+ }
+
+ /**
+ * Override to implement SecurityToken operations (background thread)
+ */
+ protected void doSecurityTokenInBackground() throws IOException {
+ mSecurityTokenFingerprints = mSecurityTokenHelper.getFingerprints();
+ mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
+ mSecurityTokenAid = mSecurityTokenHelper.getAid();
+ }
+
+ /**
+ * Override to handle result of SecurityToken operations (UI thread)
+ */
+ protected void onSecurityTokenPostExecute() {
+
+ final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mSecurityTokenFingerprints);
+
+ try {
+ CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
+ long masterKeyId = ring.getMasterKeyId();
+
+ Intent intent = new Intent(this, ViewKeyActivity.class);
+ intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
+ 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_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
+ intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_FINGERPRINTS, mSecurityTokenFingerprints);
+ startActivity(intent);
+ }
+ }
+
+ /**
+ * Override to use something different than Notify (UI thread)
+ */
+ 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 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)
+ 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();
+ onSecurityTokenPreExecute();
+ }
+
+ @Override
+ protected IOException doInBackground(Void... params) {
+ try {
+ handleSecurityToken(transport);
+ } catch (IOException e) {
+ return e;
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(IOException exception) {
+ super.onPostExecute(exception);
+
+ if (exception != null) {
+ handleSecurityTokenError(exception);
+ return;
+ }
+
+ onSecurityTokenPostExecute();
+ }
+ }.execute();
+ }
+
+ protected void pauseTagHandling() {
+ mTagHandlingEnabled = false;
+ }
+
+ protected void resumeTagHandling() {
+ mTagHandlingEnabled = true;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mNfcTagDispatcher = 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) {
+ // Restore value of members from saved state
+ mTagHandlingEnabled = savedInstanceState.getBoolean(EXTRA_TAG_HANDLING_ENABLED);
+ } else {
+ mTagHandlingEnabled = true;
+ }
+
+ Intent intent = getIntent();
+ String action = intent.getAction();
+ if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
+ throw new AssertionError("should not happen: NfcOperationActivity.onCreate is called instead of onNewIntent!");
+ }
+
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putBoolean(EXTRA_TAG_HANDLING_ENABLED, mTagHandlingEnabled);
+ }
+
+ /**
+ * This activity is started as a singleTop activity.
+ * All new NFC Intents which are delivered to this activity are handled here
+ */
+ @Override
+ public void onNewIntent(final Intent intent) {
+ mNfcTagDispatcher.interceptIntent(intent);
+ }
+
+ private void handleSecurityTokenError(IOException e) {
+
+ if (e instanceof TagLostException) {
+ onSecurityTokenError(getString(R.string.security_token_error_tag_lost));
+ return;
+ }
+
+ if (e instanceof IsoDepNotSupportedException) {
+ onSecurityTokenError(getString(R.string.security_token_error_iso_dep_not_supported));
+ return;
+ }
+
+ short status;
+ if (e instanceof CardException) {
+ status = ((CardException) e).getResponseCode();
+ } else {
+ status = -1;
+ }
+
+ // Wrong PIN, a status of 63CX indicates X attempts remaining.
+ // NOTE: Used in ykneo-openpgp version < 1.0.10, changed to 0x6982 in 1.0.11
+ // https://github.com/Yubico/ykneo-openpgp/commit/90c2b91e86fb0e43ee234dd258834e75e3416410
+ if ((status & (short) 0xFFF0) == 0x63C0) {
+ int tries = status & 0x000F;
+ // hook to do something different when PIN is wrong
+ onSecurityTokenPinError(getResources().getQuantityString(R.plurals.security_token_error_pin, tries, tries));
+ return;
+ }
+
+ // Otherwise, all status codes are fixed values.
+ switch (status) {
+
+ // These error conditions are likely to be experienced by an end user.
+
+ /* OpenPGP Card Spec: Security status not satisfied, PW wrong,
+ PW not checked (command not allowed), Secure messaging incorrect (checksum and/or cryptogram) */
+ // NOTE: Used in ykneo-openpgp >= 1.0.11 for wrong PIN
+ case 0x6982: {
+ // hook to do something different when PIN is wrong
+ onSecurityTokenPinError(getString(R.string.security_token_error_security_not_satisfied));
+ break;
+ }
+ /* OpenPGP Card Spec: Selected file in termination state */
+ case 0x6285: {
+ onSecurityTokenError(getString(R.string.security_token_error_terminated));
+ break;
+ }
+ /* OpenPGP Card Spec: Wrong length (Lc and/or Le) */
+ // NOTE: Used in ykneo-openpgp < 1.0.10 for too short PIN, changed in 1.0.11 to 0x6A80 for too short PIN
+ // https://github.com/Yubico/ykneo-openpgp/commit/b49ce8241917e7c087a4dab7b2c755420ff4500f
+ case 0x6700: {
+ // hook to do something different when PIN is wrong
+ onSecurityTokenPinError(getString(R.string.security_token_error_wrong_length));
+ break;
+ }
+ /* OpenPGP Card Spec: Incorrect parameters in the data field */
+ // NOTE: Used in ykneo-openpgp >= 1.0.11 for too short PIN
+ case 0x6A80: {
+ // hook to do something different when PIN is wrong
+ onSecurityTokenPinError(getString(R.string.security_token_error_bad_data));
+ break;
+ }
+ /* OpenPGP Card Spec: Authentication method blocked, PW blocked (error counter zero) */
+ case 0x6983: {
+ onSecurityTokenError(getString(R.string.security_token_error_authentication_blocked));
+ break;
+ }
+ /* OpenPGP Card Spec: Condition of use not satisfied */
+ case 0x6985: {
+ onSecurityTokenError(getString(R.string.security_token_error_conditions_not_satisfied));
+ break;
+ }
+ /* OpenPGP Card Spec: SM data objects incorrect (e.g. wrong TLV-structure in command data) */
+ // NOTE: 6A88 is "Not Found" in the spec, but ykneo-openpgp also returns 6A83 for this in some cases.
+ case 0x6A88:
+ case 0x6A83: {
+ 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: {
+ onSecurityTokenError(getString(R.string.security_token_error_unknown));
+ break;
+ }
+ // 6A82 app not installed on security token!
+ case 0x6A82: {
+ if (mSecurityTokenHelper.isFidesmoToken()) {
+ // Check if the Fidesmo app is installed
+ if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) {
+ promptFidesmoPgpInstall();
+ } else {
+ promptFidesmoAppInstall();
+ }
+ } else { // Other (possibly) compatible hardware
+ onSecurityTokenError(getString(R.string.security_token_error_pgp_app_not_installed));
+ }
+ break;
+ }
+
+ // 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.
+
+ /* OpenPGP Card Spec: Last command of the chain expected */
+ case 0x6883: {
+ onSecurityTokenError(getString(R.string.security_token_error_chaining_error));
+ break;
+ }
+ /* OpenPGP Card Spec: Wrong parameters P1-P2 */
+ case 0x6B00: {
+ onSecurityTokenError(getString(R.string.security_token_error_header, "P1/P2"));
+ break;
+ }
+ /* OpenPGP Card Spec: Instruction (INS) not supported */
+ case 0x6D00: {
+ onSecurityTokenError(getString(R.string.security_token_error_header, "INS"));
+ break;
+ }
+ /* OpenPGP Card Spec: Class (CLA) not supported */
+ case 0x6E00: {
+ onSecurityTokenError(getString(R.string.security_token_error_header, "CLA"));
+ break;
+ }
+ default: {
+ onSecurityTokenError(getString(R.string.security_token_error, e.getMessage()));
+ break;
+ }
+ }
+
+ }
+
+ /**
+ * Called when the system is about to start resuming a previous activity,
+ * disables NFC Foreground Dispatch
+ */
+ public void onPause() {
+ super.onPause();
+ Log.d(Constants.TAG, "BaseNfcActivity.onPause");
+
+ mNfcTagDispatcher.disableExclusiveNfc();
+ }
+
+ /**
+ * Called when the activity will start interacting with the user,
+ * enables NFC Foreground Dispatch
+ */
+ public void onResume() {
+ super.onResume();
+ Log.d(Constants.TAG, "BaseNfcActivity.onResume");
+ mNfcTagDispatcher.enableExclusiveNfc();
+ }
+
+ protected void obtainSecurityTokenPin(RequiredInputParcel requiredInput) {
+
+ try {
+ Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this,
+ requiredInput.getMasterKeyId(), requiredInput.getSubKeyId());
+ if (passphrase != null) {
+ mSecurityTokenHelper.setPin(passphrase);
+ return;
+ }
+
+ Intent intent = new Intent(this, PassphraseDialogActivity.class);
+ intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT,
+ RequiredInputParcel.createRequiredPassphrase(requiredInput));
+ startActivityForResult(intent, REQUEST_CODE_PIN);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new AssertionError(
+ "tried to find passphrase for non-existing key. this is a programming error!");
+ }
+
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_PIN: {
+ if (resultCode != Activity.RESULT_OK) {
+ setResult(resultCode);
+ finish();
+ return;
+ }
+ CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
+ mSecurityTokenHelper.setPin(input.getPassphrase());
+ break;
+ }
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ 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();
+ }
+ doSecurityTokenInBackground();
+ }
+
+ public boolean isSecurityTokenConnected() {
+ return mSecurityTokenHelper.isConnected();
+ }
+
+ public static class IsoDepNotSupportedException extends IOException {
+
+ public IsoDepNotSupportedException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ }
+
+ /**
+ * Ask user if she wants to install PGP onto her Fidesmo token
+ */
+ private void promptFidesmoPgpInstall() {
+ FidesmoPgpInstallDialog fidesmoPgpInstallDialog = new FidesmoPgpInstallDialog();
+ fidesmoPgpInstallDialog.show(getSupportFragmentManager(), "fidesmoPgpInstallDialog");
+ }
+
+ /**
+ * Show a Dialog to the user informing that Fidesmo App must be installed and with option
+ * to launch the Google Play store.
+ */
+ private void promptFidesmoAppInstall() {
+ 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;
+ try {
+ mPackageManager.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
+ mAppInstalled = true;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(Constants.TAG, "App not installed on Android device");
+ mAppInstalled = false;
+ }
+ 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/BaseSecurityTokenNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java
deleted file mode 100644
index ae1944ced..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java
+++ /dev/null
@@ -1,1057 +0,0 @@
-/*
- * Copyright (C) 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.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.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-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.ui.CreateKeyActivity;
-import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
-import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
-import org.sufficientlysecure.keychain.ui.dialog.FidesmoInstallDialog;
-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 {
- 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 TagDispatcher mTagDispatcher;
- private IsoCard mIsoCard;
- private boolean mTagHandlingEnabled;
-
- private static final int TIMEOUT = 100000;
-
- private byte[] mNfcFingerprints;
- private String mNfcUserId;
- private byte[] mNfcAid;
-
- /**
- * Override to change UI before NFC handling (UI thread)
- */
- protected void onNfcPreExecute() {
- }
-
- /**
- * Override to implement NFC operations (background thread)
- */
- protected void doNfcInBackground() throws IOException {
- mNfcFingerprints = nfcGetFingerprints();
- mNfcUserId = nfcGetUserId();
- mNfcAid = nfcGetAid();
- }
-
- /**
- * Override to handle result of NFC operations (UI thread)
- */
- protected void onNfcPostExecute() {
-
- final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
-
- try {
- CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
- long masterKeyId = ring.getMasterKeyId();
-
- 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);
- 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);
- startActivity(intent);
- }
- }
-
- /**
- * Override to use something different than Notify (UI thread)
- */
- protected void onNfcError(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);
- }
-
- public void tagDiscovered(final Tag tag) {
- // Actual NFC 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();
- }
-
- @Override
- protected IOException doInBackground(Void... params) {
- try {
- handleTagDiscovered(tag);
- } catch (IOException e) {
- return e;
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(IOException exception) {
- super.onPostExecute(exception);
-
- if (exception != null) {
- handleNfcError(exception);
- return;
- }
-
- onNfcPostExecute();
- }
- }.execute();
- }
-
- protected void pauseTagHandling() {
- mTagHandlingEnabled = false;
- }
-
- protected void resumeTagHandling() {
- mTagHandlingEnabled = true;
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mTagDispatcher = TagDispatcher.get(this, this, false, false);
-
- // Check whether we're recreating a previously destroyed instance
- if (savedInstanceState != null) {
- // Restore value of members from saved state
- mTagHandlingEnabled = savedInstanceState.getBoolean(EXTRA_TAG_HANDLING_ENABLED);
- } else {
- mTagHandlingEnabled = true;
- }
-
- Intent intent = getIntent();
- String action = intent.getAction();
- if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
- throw new AssertionError("should not happen: NfcOperationActivity.onCreate is called instead of onNewIntent!");
- }
-
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- outState.putBoolean(EXTRA_TAG_HANDLING_ENABLED, mTagHandlingEnabled);
- }
-
- /**
- * This activity is started as a singleTop activity.
- * All new NFC Intents which are delivered to this activity are handled here
- */
- @Override
- public void onNewIntent(final Intent intent) {
- mTagDispatcher.interceptIntent(intent);
- }
-
- private void handleNfcError(IOException e) {
-
- if (e instanceof TagLostException) {
- onNfcError(getString(R.string.security_token_error_tag_lost));
- return;
- }
-
- if (e instanceof IsoDepNotSupportedException) {
- onNfcError(getString(R.string.security_token_error_iso_dep_not_supported));
- return;
- }
-
- short status;
- if (e instanceof CardException) {
- status = ((CardException) e).getResponseCode();
- } else {
- status = -1;
- }
-
- // Wrong PIN, a status of 63CX indicates X attempts remaining.
- 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));
- return;
- }
-
- // Otherwise, all status codes are fixed values.
- switch (status) {
- // 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));
- break;
- }
- case 0x6883: {
- onNfcError(getString(R.string.security_token_error_chaining_error));
- break;
- }
- case 0x6B00: {
- onNfcError(getString(R.string.security_token_error_header, "P1/P2"));
- break;
- }
- case 0x6D00: {
- onNfcError(getString(R.string.security_token_error_header, "INS"));
- break;
- }
- case 0x6E00: {
- onNfcError(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));
- break;
- }
- case 0x6700: {
- onNfcPinError(getString(R.string.security_token_error_wrong_length));
- break;
- }
- case 0x6982: {
- onNfcError(getString(R.string.security_token_error_security_not_satisfied));
- break;
- }
- case 0x6983: {
- onNfcError(getString(R.string.security_token_error_authentication_blocked));
- break;
- }
- case 0x6985: {
- onNfcError(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));
- 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));
- break;
- }
- // 6A82 app not installed on security token!
- case 0x6A82: {
- if (isFidesmoDevice()) {
- // Check if the Fidesmo app is installed
- if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) {
- promptFidesmoPgpInstall();
- } else {
- promptFidesmoAppInstall();
- }
- } else { // Other (possibly) compatible hardware
- onNfcError(getString(R.string.security_token_error_pgp_app_not_installed));
- }
- break;
- }
- default: {
- onNfcError(getString(R.string.security_token_error, e.getMessage()));
- break;
- }
- }
-
- }
-
- /**
- * Called when the system is about to start resuming a previous activity,
- * disables NFC Foreground Dispatch
- */
- public void onPause() {
- super.onPause();
- Log.d(Constants.TAG, "BaseNfcActivity.onPause");
-
- mTagDispatcher.disableExclusiveNfc();
- }
-
- /**
- * Called when the activity will start interacting with the user,
- * enables NFC Foreground Dispatch
- */
- public void onResume() {
- super.onResume();
- Log.d(Constants.TAG, "BaseNfcActivity.onResume");
- mTagDispatcher.enableExclusiveNfc();
- }
-
- protected void obtainSecurityTokenPin(RequiredInputParcel requiredInput) {
-
- try {
- Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this,
- requiredInput.getMasterKeyId(), requiredInput.getSubKeyId());
- if (passphrase != null) {
- mPin = passphrase;
- return;
- }
-
- Intent intent = new Intent(this, PassphraseDialogActivity.class);
- intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT,
- RequiredInputParcel.createRequiredPassphrase(requiredInput));
- startActivityForResult(intent, REQUEST_CODE_PIN);
- } catch (KeyNotFoundException e) {
- throw new AssertionError(
- "tried to find passphrase for non-existing key. this is a programming error!");
- }
-
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_PIN: {
- if (resultCode != Activity.RESULT_OK) {
- setResult(resultCode);
- finish();
- return;
- }
- CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
- mPin = input.getPassphrase();
- break;
- }
- default:
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
-
- /** 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)");
- }
- 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();
-
- }
-
- public boolean isNfcConnected() {
- return mIsoCard.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 IsoDepNotSupportedException(String detailMessage) {
- super(detailMessage);
- }
-
- }
-
- 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
- */
- private void promptFidesmoPgpInstall() {
- FidesmoPgpInstallDialog mFidesmoPgpInstallDialog = new FidesmoPgpInstallDialog();
- mFidesmoPgpInstallDialog.show(getSupportFragmentManager(), "mFidesmoPgpInstallDialog");
- }
-
- /**
- * Show a Dialog to the user informing that Fidesmo App must be installed and with option
- * to launch the Google Play store.
- */
- private void promptFidesmoAppInstall() {
- FidesmoInstallDialog mFidesmoInstallDialog = new FidesmoInstallDialog();
- mFidesmoInstallDialog.show(getSupportFragmentManager(), "mFidesmoInstallDialog");
- }
-
- /**
- * 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;
- try {
- mPackageManager.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
- mAppInstalled = true;
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(Constants.TAG, "App not installed on Android device");
- mAppInstalled = false;
- }
- return mAppInstalled;
- }
-}
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..4074d391b 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
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.ui.widget;
import android.content.Context;
import android.database.Cursor;
+import android.graphics.Color;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
@@ -83,6 +84,11 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView<KeyItem>
LayoutInflater l = LayoutInflater.from(getContext());
View view = l.inflate(R.layout.recipient_box_entry, null);
((TextView) view.findViewById(android.R.id.text1)).setText(keyItem.getReadableName());
+
+ if (keyItem.mIsRevoked || !keyItem.mHasEncrypt || keyItem.mIsExpired) {
+ ((TextView) view.findViewById(android.R.id.text1)).setTextColor(Color.RED);
+ }
+
return view;
}
@@ -171,4 +177,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..7a8e65ae4
--- /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.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: {
+ 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 permission 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-cs/help_about.md b/OpenKeychain/src/main/res/raw-cs/help_about.md
index 4e8f0270b..fad35b356 100644
--- a/OpenKeychain/src/main/res/raw-cs/help_about.md
+++ b/OpenKeychain/src/main/res/raw-cs/help_about.md
@@ -65,7 +65,7 @@ License: GPLv3+
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-cs/help_changelog.md b/OpenKeychain/src/main/res/raw-cs/help_changelog.md
index b3aab989e..629931b2c 100644
--- a/OpenKeychain/src/main/res/raw-cs/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-cs/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-de/help_about.md b/OpenKeychain/src/main/res/raw-de/help_about.md
index fa75bed0e..ffd97656e 100644
--- a/OpenKeychain/src/main/res/raw-de/help_about.md
+++ b/OpenKeychain/src/main/res/raw-de/help_about.md
@@ -65,7 +65,7 @@ Lizenz: GPLv3+
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache-Lizenz v2)
* [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT-Lizenz)
* [Snackbar](https://github.com/nispok/snackbar) (MIT-Lizenz)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT-X11-Lizenz)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 Lizenz)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache-Lizenz v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache-Lizenz v2)
* [ZXing](https://github.com/zxing/zxing) (Apache-Lizenz v2)
diff --git a/OpenKeychain/src/main/res/raw-de/help_changelog.md b/OpenKeychain/src/main/res/raw-de/help_changelog.md
index bb84548ee..4070b5ae1 100644
--- a/OpenKeychain/src/main/res/raw-de/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-de/help_changelog.md
@@ -1,10 +1,17 @@
[//]: # (Beachte: Bitte schreibe jeden Satz in eine eigene Zeile, Transifex wird jede Zeile in ein eigenes Übesetzungsfeld setzen!)
+## 3.9
+
+ * Erkennung und Bearbeitung von Textdaten
+ * Performanceverbesserungen
+ * Verbesserung der Benutzeroberfläche zur Handhabung von Smartcards
+
+
## 3.8
* Bearbeiten von Schlüsseln überarbeitet
* Wähle die Zeit wie lange dein Passwort erinnert wird beim jedem Eingeben
- * Facebook key import
+ Facebook Schlüsselimport
## 3.7
diff --git a/OpenKeychain/src/main/res/raw-es-rMX/help_about.md b/OpenKeychain/src/main/res/raw-es-rMX/help_about.md
index 463d3315c..f72cb8cb4 100644
--- a/OpenKeychain/src/main/res/raw-es-rMX/help_about.md
+++ b/OpenKeychain/src/main/res/raw-es-rMX/help_about.md
@@ -65,7 +65,7 @@ Licencia: GPLv3+
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Licencia Apache v2)
* [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (Licencia MIT)
* [Snackbar](https://github.com/nispok/snackbar) (Licencia MIT)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (Licencia MIT X11)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licencia Apache v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licencia Apache v2)
* [ZXing](https://github.com/zxing/zxing) (Licencia Apache v2)
diff --git a/OpenKeychain/src/main/res/raw-es-rMX/help_changelog.md b/OpenKeychain/src/main/res/raw-es-rMX/help_changelog.md
index 99a093c89..5148c17a8 100644
--- a/OpenKeychain/src/main/res/raw-es-rMX/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-es-rMX/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (Observe: ¡Por favor ingrese cada enunciado en su propia línea, Transifex coloca cada línea en su propio campo de traducción!)
+## 3.9
+
+ * Detection and handling of text data
+ * Performance improvements
+ * UI improvements for Security Token handling
+
+
## 3.8
* Redesigned key editing
diff --git a/OpenKeychain/src/main/res/raw-es/help_about.md b/OpenKeychain/src/main/res/raw-es/help_about.md
index 9c82cae5a..44a544694 100644
--- a/OpenKeychain/src/main/res/raw-es/help_about.md
+++ b/OpenKeychain/src/main/res/raw-es/help_about.md
@@ -65,7 +65,7 @@ Licencia: GPLv3+
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Licencia Apache v2)
* [Librería SafeSlinger Exchange](https://github.com/SafeSlingerProject/exchange-android) (Licencia MIT)
* [Snackbar](https://github.com/nispok/snackbar) (Licencia MIT)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 License)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licencia Apache v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licencia Apache v2)
* [ZXing](https://github.com/zxing/zxing) (Licencia Apache v2)
diff --git a/OpenKeychain/src/main/res/raw-es/help_changelog.md b/OpenKeychain/src/main/res/raw-es/help_changelog.md
index ca3c09563..8198ec9a2 100644
--- a/OpenKeychain/src/main/res/raw-es/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-es/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (NOTA: ¡Por favor ponga cada frase en su propia línea, Transifex pone cada línea en su propio campo de traducción!)
+## 3.9
+
+ * Detection and handling of text data
+ * Performance improvements
+ * UI improvements for Security Token handling
+
+
## 3.8
* Redesigned key editing
diff --git a/OpenKeychain/src/main/res/raw-eu/advanced.md b/OpenKeychain/src/main/res/raw-eu/advanced.md
index 54a694084..3f2a00127 100644
--- a/OpenKeychain/src/main/res/raw-eu/advanced.md
+++ b/OpenKeychain/src/main/res/raw-eu/advanced.md
@@ -1,9 +1,9 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (OHARRA: Mesedez jarri esaldi bakoitza bere lerroan, Transifex-ek lerroak bere itzulpen eremuan jartzen ditu!)
-Advanced screen allows you to
-* share key in non-recommended ways
-* edit identities
-* edit subkeys
-* examine certificates in detail
+Ikusleiho aurreratuak ahalbidetzen dizu
+* giltza elkarbanatzea bide ez-gomendatuan
+* nortasunak editatzea
+* azpigiltzak editatzea
+* egiaztagiriak xehetasunez aztartzea
-Only proceed if you know what you are doing! \ No newline at end of file
+Zer egiten ari zaren badakizu besterik ez jarraitu! \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-eu/help_about.md b/OpenKeychain/src/main/res/raw-eu/help_about.md
index 13925d9ea..bd373f1e3 100644
--- a/OpenKeychain/src/main/res/raw-eu/help_about.md
+++ b/OpenKeychain/src/main/res/raw-eu/help_about.md
@@ -65,7 +65,7 @@ Baimena: GPLv3+
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Diseinua) (Apache Baimena 2 bertsioa)
* [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT Baimena)
* [Snackbar](https://github.com/nispok/snackbar) (MIT Baimena)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 Baimena)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 Baimena)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache Baimena v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache Baimena v2)
* [ZXing](https://github.com/zxing/zxing) (Apache Baimena v2)
diff --git a/OpenKeychain/src/main/res/raw-eu/help_changelog.md b/OpenKeychain/src/main/res/raw-eu/help_changelog.md
index 2d52f8953..1a1be91fc 100644
--- a/OpenKeychain/src/main/res/raw-eu/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-eu/help_changelog.md
@@ -1,10 +1,17 @@
[//]: # (OHARRA: Meseez jarri esaldi bakoitza bere lerroan, Transifex-ek lerroak bere itzulpen eremuan jartzen ditu!)
+## 3.9
+
+ * Idazki datuen atzematea eta kudeaketa
+ * Egintza hobekuntzak
+ * EI hobekuntzak Segurtasun Lekukoaren kudeaketarako
+
+
## 3.8
- * Redesigned key editing
- * Choose remember time individually when entering passwords
- * Facebook key import
+ * Giltza edizio berdiseinatua
+ * Hautatu ordua banaka sarhitzak idazterakoan
+ * Facebook giltza inportazioa
## 3.7
diff --git a/OpenKeychain/src/main/res/raw-fa/help_about.md b/OpenKeychain/src/main/res/raw-fa/help_about.md
index 833295cbb..1543a4dcb 100644
--- a/OpenKeychain/src/main/res/raw-fa/help_about.md
+++ b/OpenKeychain/src/main/res/raw-fa/help_about.md
@@ -65,7 +65,7 @@
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-fa/help_changelog.md b/OpenKeychain/src/main/res/raw-fa/help_changelog.md
index 464bdc61b..87544a346 100644
--- a/OpenKeychain/src/main/res/raw-fa/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-fa/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (تذکر: هر جمله در همان خط!)
+## 3.9
+
+ * Detection and handling of text data
+ * Performance improvements
+ * UI improvements for Security Token handling
+
+
## 3.8
* Redesigned key editing
diff --git a/OpenKeychain/src/main/res/raw-fi/help_about.md b/OpenKeychain/src/main/res/raw-fi/help_about.md
index 4e8f0270b..fad35b356 100644
--- a/OpenKeychain/src/main/res/raw-fi/help_about.md
+++ b/OpenKeychain/src/main/res/raw-fi/help_about.md
@@ -65,7 +65,7 @@ License: GPLv3+
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-fi/help_changelog.md b/OpenKeychain/src/main/res/raw-fi/help_changelog.md
index b3aab989e..629931b2c 100644
--- a/OpenKeychain/src/main/res/raw-fi/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-fi/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-fr/help_about.md b/OpenKeychain/src/main/res/raw-fr/help_about.md
index 4522af54b..553ab16b6 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_about.md
+++ b/OpenKeychain/src/main/res/raw-fr/help_about.md
@@ -65,7 +65,7 @@ Licence : GPLv3+
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Conception matérielle)</a> (Licence Apache v2)
* [Bibliothèque d'échange SafeSlinger](https://github.com/SafeSlingerProject/exchange-android) (Licence MIT)
* [Snackbar](https://github.com/nispok/snackbar) (Licence MIT)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (Licence MIT X11)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (Licence MIT X11)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licence Apache v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licence Apache v2)
* [ZXing](https://github.com/zxing/zxing) (Licence Apache v2)
diff --git a/OpenKeychain/src/main/res/raw-fr/help_changelog.md b/OpenKeychain/src/main/res/raw-fr/help_changelog.md
index ff0c610b7..b815516a3 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-fr/help_changelog.md
@@ -1,10 +1,17 @@
[//] : # (NOTE : veuillez mettre chaque phrase sur sa propre ligne. Transifex met chaque ligne dans son propre champ de traduction !)
+## 3.9
+
+ * 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
* Nouvelle conception de la modification des clefs
* Choisir les délais de mémorisation individuellement lors de la saisie des mots de passe
- * Facebook key import
+ * Importation de clef Facebook
## 3.7
diff --git a/OpenKeychain/src/main/res/raw-hi/help_about.md b/OpenKeychain/src/main/res/raw-hi/help_about.md
index 4e8f0270b..fad35b356 100644
--- a/OpenKeychain/src/main/res/raw-hi/help_about.md
+++ b/OpenKeychain/src/main/res/raw-hi/help_about.md
@@ -65,7 +65,7 @@ License: GPLv3+
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-hi/help_changelog.md b/OpenKeychain/src/main/res/raw-hi/help_changelog.md
index b3aab989e..629931b2c 100644
--- a/OpenKeychain/src/main/res/raw-hi/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-hi/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-hu/help_about.md b/OpenKeychain/src/main/res/raw-hu/help_about.md
index 4e8f0270b..fad35b356 100644
--- a/OpenKeychain/src/main/res/raw-hu/help_about.md
+++ b/OpenKeychain/src/main/res/raw-hu/help_about.md
@@ -65,7 +65,7 @@ License: GPLv3+
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-hu/help_changelog.md b/OpenKeychain/src/main/res/raw-hu/help_changelog.md
index b3aab989e..629931b2c 100644
--- a/OpenKeychain/src/main/res/raw-hu/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-hu/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-id/advanced.md b/OpenKeychain/src/main/res/raw-id/advanced.md
index 54a694084..bf3daf41e 100644
--- a/OpenKeychain/src/main/res/raw-id/advanced.md
+++ b/OpenKeychain/src/main/res/raw-id/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
+Layar Advanced memungkinkan Anda untuk
+* membagikan kunci dengan cara yang tidak disarankan
+* mengedit identitas
+* mengedit kunci tambahan
+* memeriksa sertifikat dengan lebih detail
-Only proceed if you know what you are doing! \ No newline at end of file
+Lanjutkan hanya jika Anda mengetahui apa yang Anda lakukan! \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-id/help_about.md b/OpenKeychain/src/main/res/raw-id/help_about.md
index 7c8b894c2..2ec7f239e 100644
--- a/OpenKeychain/src/main/res/raw-id/help_about.md
+++ b/OpenKeychain/src/main/res/raw-id/help_about.md
@@ -65,7 +65,7 @@ Lisensi: GPLv3+
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-id/help_changelog.md b/OpenKeychain/src/main/res/raw-id/help_changelog.md
index d18fbb1a0..114f1e493 100644
--- a/OpenKeychain/src/main/res/raw-id/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-id/help_changelog.md
@@ -1,10 +1,17 @@
[//]: # (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
+ * Perubahan design pengeditan kunci
+ * Pilih waktu mengingat kata sandi secara individu
+ * Import kunci Facebook
## 3.7
@@ -17,68 +24,68 @@
* Backup yang terenkripsi
* Perbaikan keamanan berdasarkan audit keamanan eksternal
- * YubiKey NEO key creation wizard
+ * Pengaturan pembuatan kunci YubiKey NEO
* Dukungan basic internal MIME
- * Automatic key synchronization
- * Experimental feature: link keys to Github, Twitter accounts
- * Experimental feature: key confirmation via phrases
- * Experimental feature: dark theme
- * API: Version 9
+ * Sinkronisasi kunci otomatis
+ * Fitur eksperimental: hubungkan kunci ke akun Github dan Twitter
+ * Fitur experimental: konfirmasi kunci menggunakan frasa
+ * Fitur eksperimental: tema gelap
+ * API: Versi 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
+ * Pencabutan kunci saat penghapusan
+ * Pembaharuan pengecekan untuk kriptografi yang tidak aman
+ * Perbaikan: Jangan tutup OpenKeychain setelah pengaturan pertama selesai
+ * API: Versi 8
## 3.4
- * Anonymous key download over Tor
- * Proxy support
- * Better YubiKey error handling
+ * Unduh kunci secara anonim melalui Tor
+ * Dukungan proxy
+ * Penanganan error YubiKey yang lebih baik
## 3.3
- * New decryption screen
- * Decryption of multiple files at once
- * Better handling of YubiKey errors
+ * Layar dekripsi yang baru
+ * Dekripsi banyak file dalam satu kali
+ * Penanganan error YubiKey yang lebih baik
## 3.2
- * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
+ * Versi pertama dengan dukungan penuh YubiKey dari antarmuka pengguna: Edit kunci, hubungkan YubiKey dengan kunci,...
* 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
+ * Intregrasi pemindai kode QR (Izin baru dibutuhkan)
+ * Pengaturan pembuatan kunci yang lebih baik
+ * Perbaikan kehilangan kontak setelah sinkronasi
+ * Membutuhkan Android 4
+ * Design baru layar kunci
+ * Meringkas preferensi kripto, pilihan sandi aman yang lebih baik
+ * API: Tandatangan terpisah, pilihan bebas kunci tandatangan,...
+ * Perbaikan: Beberapa kunci valid ditunjukan sebagai expired atau dicabut
+ * Tidak menerima tandatangan dari kunci tambahan yang expired atau dicabut
+ * Dukungan Keybase.io di pilihan lanjutan
+ * Cara untuk memperbaharui semua kunci sekaligus
## 3.1.2
- * Fix key export to files (now for real)
+ * Perbaikan export kunci ke file (beres)
## 3.1.1
- * Fix key export to files (they were written partially)
- * Fix crash on Android 2.3
+ * Perbaikan export kunci ke file (sebagian)
+ * Perbaikan crash di Android 2.3
## 3.1
- * Fix crash on Android 5
+ * Perbaikan crash di Android 5
* New certify screen
* Secure Exchange directly from key list (SafeSlinger library)
* New QR Code program flow
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 8cd939be2..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)
- * [SpongyCastle](https://rtyley.github.io/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 777ac503f..100fee640 100644
--- a/OpenKeychain/src/main/res/raw-it/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-it/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (NOTA: Si prega di mettere ogni frase in una propria linea, Transifex mette ogni riga nel proprio campo di traduzione!)
+## 3.9
+
+ * Detection and handling of text data
+ * Performance improvements
+ * UI improvements for Security Token handling
+
+
## 3.8
* Redesigned key editing
@@ -10,7 +17,7 @@
## 3.7
* Improved Android 6 support (permissions, integration into text selection)
- * API: Version 10
+ * API: Versione 10
## 3.6
@@ -23,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
@@ -31,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
@@ -55,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,...
@@ -78,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
@@ -159,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
@@ -235,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
@@ -256,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
@@ -274,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
@@ -313,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_about.md b/OpenKeychain/src/main/res/raw-ja/help_about.md
index c8de63ce5..44c1a23c5 100644
--- a/OpenKeychain/src/main/res/raw-ja/help_about.md
+++ b/OpenKeychain/src/main/res/raw-ja/help_about.md
@@ -65,7 +65,7 @@
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 License)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (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-ja/help_changelog.md b/OpenKeychain/src/main/res/raw-ja/help_changelog.md
index 7491bfb4a..49e659310 100644
--- a/OpenKeychain/src/main/res/raw-ja/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-ja/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.9
+
+ * テキストデータの検出と処理
+ * パフォーマンスの改善
+ * セキュリティトークンを処理する UI の改善
+
+
## 3.8
* 鍵編集の再デザイン
diff --git a/OpenKeychain/src/main/res/raw-kn/advanced.md b/OpenKeychain/src/main/res/raw-kn/advanced.md
new file mode 100644
index 000000000..54a694084
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-kn/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-kn/help_about.md b/OpenKeychain/src/main/res/raw-kn/help_about.md
new file mode 100644
index 000000000..fad35b356
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-kn/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-kn/help_certification.md b/OpenKeychain/src/main/res/raw-kn/help_certification.md
new file mode 100644
index 000000000..3518adf73
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-kn/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-kn/help_changelog.md b/OpenKeychain/src/main/res/raw-kn/help_changelog.md
new file mode 100644
index 000000000..629931b2c
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-kn/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-kn/help_start.md b/OpenKeychain/src/main/res/raw-kn/help_start.md
new file mode 100644
index 000000000..4cc331942
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-kn/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-ko/help_about.md b/OpenKeychain/src/main/res/raw-ko/help_about.md
index f60a918da..dddbc60a3 100644
--- a/OpenKeychain/src/main/res/raw-ko/help_about.md
+++ b/OpenKeychain/src/main/res/raw-ko/help_about.md
@@ -65,7 +65,7 @@
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-ko/help_changelog.md b/OpenKeychain/src/main/res/raw-ko/help_changelog.md
index 929933144..95cf8fba4 100644
--- a/OpenKeychain/src/main/res/raw-ko/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-ko/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
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
new file mode 100644
index 000000000..53a215af6
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nb/advanced.md
@@ -0,0 +1,9 @@
+[//]: # (NOTER: Vennligst putt hver setning på sin egen linje, Transifex putter hver linje i sitt eget oversettelsesfelt!)
+
+Avansert-skjermen lar deg
+* dele nøkler på uanbefalte måter
+* slette identiteter
+* rediger undernøkler
+* undersøk sertifikatdetaljer
+
+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
new file mode 100644
index 000000000..9b9e496a6
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nb/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.
+
+Lisens: GPLv3+
+
+[//]: # (MERK: Alfabetisk rekkefølge)
+
+## Hovedutviklere
+ * Dominik Schürmann (Vedlikeholder)
+ * Vincent Breitmoser
+
+## Topp-bidragsytere
+ * Adithya Abraham Philip
+ * Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG-utvikler)
+ * Tim Bray
+
+## Periodevise bidragsytere
+ * 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'
+
+[//]: # (MERK: Alfabetisk rekkefølge)
+
+## 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)
+ * [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-nb/help_certification.md b/OpenKeychain/src/main/res/raw-nb/help_certification.md
new file mode 100644
index 000000000..19098897e
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nb/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!)
+
+## Oppsett av nøkkel
+Without confirmation, you cannot be sure if a key really corresponds to a specific person.
+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.
+
+## Status for nøkkel
+
+<img src="status_signature_verified_cutout_24dp"/>
+Bekreftet: Du har allerede bekreftet denne nøkkelen, eksempelvis ved å skanne QR-koden.
+<img src="status_signature_unverified_cutout_24dp"/>
+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"/>
+Utløpt: Denne nøkkelen er ikke lenger gyldig. Bare eieren kan forlenge dens gyldighet.
+<img src="status_signature_revoked_cutout_24dp"/>
+Utløpt: Denne nøkkelen er ikke lenger gyldig. Den har blitt tilbakekalt av eieren.
+
+## 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.
+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
new file mode 100644
index 000000000..f544486f3
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nb/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
+ * Ytelsesforbedringer
+ * Brukergrensesnittforbedringer for behandling av sikkerhetssymboler
+
+
+## 3.8
+
+ * Re-designet nøkkelredigering
+ * Choose remember time individually when entering passwords
+ * Nøkkelimport fra Facebook
+
+
+## 3.7
+
+ * Forbedret Android 6 støtte (tillatelser, ingegrering i tekstfeltet)
+ * API: Versjon 10
+
+
+## 3.6
+
+ * Krypterte sikkerhetskopier
+ * Sikkerhetsutbedringer basert på eksterne sikkerhetsgjennomganger
+ * YubiKey NEO key creation wizard
+ * Grunnleggende intern MIME-støtte
+ * Automatic key synchronization
+ * Experimental feature: link keys to Github, Twitter accounts
+ * Eksperimentell funksjon: nøkkelbekreftelse via fraser
+ * Eksperimentell funksjon: mørk drakt
+ * API: Versjon 9
+
+
+## 3.5
+
+ * Key revocation on key deletion
+ * Forbedret sjekking av usikker kryptografi
+ * Fix: Ikke lukke OpenKeychain etter at førstegangsveiviseren fullføres
+ * API: Versjon 8
+
+
+## 3.4
+
+ * Anonym nøkkel-nedlasting over Tor
+ * Mellomtjener-støtte
+ * Better YubiKey error handling
+
+
+## 3.3
+
+ * 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
+ * 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
+ * Metode for å oppdatere alle nøklene på én gang
+
+
+## 3.1.2
+
+ * Fix key export to files (now for real)
+
+
+## 3.1.1
+
+ * Fix key export to files (they were written partially)
+ * Fikser kræsj på 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)
+ * 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
+
+
+## 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
+
+ * Tysk og Italiensk oversettelse ved
+ * 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)
+ * 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
new file mode 100644
index 000000000..aa53344ff
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nb/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!)
+
+## 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. Velg "Kontoinnstillinger", rull ned til bunnen og klikk "Kryptografi".
+ 3. Klikk på "OpenPGP-tilbyder" og vel OpenKeychain fra listen.
+
+## Jeg fant en feil i OpenKeychain!
+Rapporter feil ved bruk av [feilrettingsoversikten i OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+
+## 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).
+
+## 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-nl/help_about.md b/OpenKeychain/src/main/res/raw-nl/help_about.md
index a6f22d371..7bb7d2cf9 100644
--- a/OpenKeychain/src/main/res/raw-nl/help_about.md
+++ b/OpenKeychain/src/main/res/raw-nl/help_about.md
@@ -65,7 +65,7 @@ Licentie: GPLv3+
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache licentie v2)
* [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT licentie)
* [Snackbar](https://github.com/nispok/snackbar) (MIT licentie)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 License)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache licentie v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache licentie v2)
* [ZXing](https://github.com/zxing/zxing) (Apache licentie v2)
diff --git a/OpenKeychain/src/main/res/raw-nl/help_changelog.md b/OpenKeychain/src/main/res/raw-nl/help_changelog.md
index bc03b2662..c581c968b 100644
--- a/OpenKeychain/src/main/res/raw-nl/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-nl/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-pl/help_about.md b/OpenKeychain/src/main/res/raw-pl/help_about.md
index 13fcbc185..d9930f0d7 100644
--- a/OpenKeychain/src/main/res/raw-pl/help_about.md
+++ b/OpenKeychain/src/main/res/raw-pl/help_about.md
@@ -65,7 +65,7 @@ Licencja: GPLv3+
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-pl/help_changelog.md b/OpenKeychain/src/main/res/raw-pl/help_changelog.md
index b3aab989e..629931b2c 100644
--- a/OpenKeychain/src/main/res/raw-pl/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-pl/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-pt-rBR/advanced.md b/OpenKeychain/src/main/res/raw-pt-rBR/advanced.md
new file mode 100644
index 000000000..6551e778e
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/advanced.md
@@ -0,0 +1,9 @@
+[//]: # (NOTA: coloque cada frase em sua própria linha, Transifex coloca cada linha em seu próprio campo de tradução!)
+
+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
+
+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
new file mode 100644
index 000000000..80d07423f
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/help_about.md
@@ -0,0 +1,72 @@
+[//]: # (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) é uma implementação OpenPGP para Android.
+
+Licença: GPLv3+
+
+[//]: # (NOTA: Ordem alfabética)
+
+## Desenvolvedores principais
+ * Dominik Schürmann (Mantenedor)
+ * Vincent Breitmoser
+
+## Principais Colaboradores
+ * Adithya Abraham Philip
+ * Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG desenvolvedor)
+ * Tim Bray
+
+## Colaboradores Ocasionais
+ * 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'
+
+[//]: # (NOTA: Ordem alfabética)
+
+## 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)
+ * [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-pt-rBR/help_certification.md b/OpenKeychain/src/main/res/raw-pt-rBR/help_certification.md
new file mode 100644
index 000000000..b3d48189c
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/help_certification.md
@@ -0,0 +1,28 @@
+[//]: # (NOTA: coloque cada frase em sua própria linha, Transifex coloca cada linha em seu próprio campo de tradução!)
+
+## 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.
+
+## Estado de chave
+
+<img src="status_signature_verified_cutout_24dp"/>
+Confirmado: Você já confirmou esta chave, por por exemplo, fazendo a leitura de um código QR.
+<img src="status_signature_unverified_cutout_24dp"/>
+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"/>
+Expirada: Esta chave não é mais válida. Somente o proprietário pode extender a sua validade.
+<img src="status_signature_revoked_cutout_24dp"/>
+Revogada: Esta chave não é mais válida. Ela foi revogada pelo seu proprietário.
+
+## 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."
+
+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
new file mode 100644
index 000000000..e9c28e801
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/help_changelog.md
@@ -0,0 +1,326 @@
+[//]: # (NOTA: coloque cada frase em sua própria linha, Transifex coloca cada linha em seu próprio campo de tradução!)
+
+## 3.9
+
+ * Detecção e tratamento de dados de texto
+ * Melhorias de desempenho
+ * UI melhorias para o manuseio do Token de Segurança
+
+
+## 3.8
+
+ * Edição de chave reestruturada
+ * Escolher individualmente lembretes quando digitar senhas
+ * Importar chave para o Facebook
+
+
+## 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-pt-rBR/help_start.md b/OpenKeychain/src/main/res/raw-pt-rBR/help_start.md
new file mode 100644
index 000000000..43b9c8dc3
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-pt-rBR/help_start.md
@@ -0,0 +1,16 @@
+[//]: # (NOTA: coloque cada frase em sua própria linha, Transifex coloca cada linha em seu próprio campo de tradução!)
+
+## 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.
+
+## 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).
+
+## 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).
+
+## 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 7d6e99951..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,9 +63,9 @@
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
* [ZXing](https://github.com/zxing/zxing) (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 787588b05..34c361ea3 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-ru/help_certification.md
@@ -1,7 +1,7 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
## Подтверждение ключей
-Без подтверждения Вы не можете быть уверены, что ключ принадлежит определенному человеку.
+Без подтверждения вы не можете быть уверены, что ключ принадлежит определённому человеку.
Простейший способ подтвердить ключ — отсканировать QR-код или получить его через NFC.
Для подтверждения ключей более чем двух человек, мы рекомендуем использовать один из доступных методов обмена ключами.
@@ -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-ru/help_changelog.md b/OpenKeychain/src/main/res/raw-ru/help_changelog.md
index b3aab989e..b76586fe2 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-ru/help_changelog.md
@@ -1,10 +1,17 @@
[//]: # (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
+ * Импорт ключа с Facebook
## 3.7
@@ -15,21 +22,21 @@
## 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
diff --git a/OpenKeychain/src/main/res/raw-ru/help_start.md b/OpenKeychain/src/main/res/raw-ru/help_start.md
index 0f12fde29..dd345c2d5 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_start.md
+++ b/OpenKeychain/src/main/res/raw-ru/help_start.md
@@ -1,6 +1,6 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-## Как мне активировать OpenKeychain в K-9 Mail?
+Как мне активировать OpenKeychain в K-9 Mail?
Для использования OpenKeychain с K-9 Mail, необходимо выполнить следующие шаги:
1. Откройте K-9 Mail и долгим нажатием выберите учетную запись, с которой будет использоваться OpenKeychain.
2. Выберите "Настройки учетной записи" и опуститесь в самый конец меню до пункта "Шифрование".
diff --git a/OpenKeychain/src/main/res/raw-sl/help_about.md b/OpenKeychain/src/main/res/raw-sl/help_about.md
index d7ce930cc..bee93f210 100644
--- a/OpenKeychain/src/main/res/raw-sl/help_about.md
+++ b/OpenKeychain/src/main/res/raw-sl/help_about.md
@@ -65,7 +65,7 @@ Licenca: GPLv3+
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
* [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (Licenca MIT)
* [Snackbar](https://github.com/nispok/snackbar) (Licenca MIT)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 License)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licenca Apache v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licenca Apache v2)
* [ZXing](https://github.com/zxing/zxing) (Licenca Apache v2)
diff --git a/OpenKeychain/src/main/res/raw-sl/help_changelog.md b/OpenKeychain/src/main/res/raw-sl/help_changelog.md
index b3aab989e..629931b2c 100644
--- a/OpenKeychain/src/main/res/raw-sl/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-sl/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-sr/help_about.md b/OpenKeychain/src/main/res/raw-sr/help_about.md
index ad9cdc231..f6eecb622 100644
--- a/OpenKeychain/src/main/res/raw-sr/help_about.md
+++ b/OpenKeychain/src/main/res/raw-sr/help_about.md
@@ -65,7 +65,7 @@
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Материјал дизајн) (Апачи лиценца в2)
* [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (МИТ лиценца)
* [Snackbar](https://github.com/nispok/snackbar) (МИТ лиценца)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (МИТ Икс11 лиценца)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (МИТ Икс11 лиценца)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Апачи лиценца в2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Апачи лиценца в2)
* [ZXing](https://github.com/zxing/zxing) (Апачи лиценца в2)
diff --git a/OpenKeychain/src/main/res/raw-sr/help_changelog.md b/OpenKeychain/src/main/res/raw-sr/help_changelog.md
index f17e4f74b..1661e5569 100644
--- a/OpenKeychain/src/main/res/raw-sr/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-sr/help_changelog.md
@@ -1,5 +1,12 @@
[//]: #
+## 3.9
+
+ * Откривање и руковање текстуалним подацима
+ * Побољшање перформанси
+ * Побољшања УИ-ја за руковање безбедносним токеном
+
+
## 3.8
* Редизајн уређивања кључа
diff --git a/OpenKeychain/src/main/res/raw-sv/advanced.md b/OpenKeychain/src/main/res/raw-sv/advanced.md
index 54a694084..8cf50c854 100644
--- a/OpenKeychain/src/main/res/raw-sv/advanced.md
+++ b/OpenKeychain/src/main/res/raw-sv/advanced.md
@@ -1,4 +1,4 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTERING: Var vänlig och sätt varje mening på sin egen rad, Transifex sätter varje rad i sitt eget fält för översättningar!)
Advanced screen allows you to
* share key in non-recommended ways
diff --git a/OpenKeychain/src/main/res/raw-sv/help_about.md b/OpenKeychain/src/main/res/raw-sv/help_about.md
index 161decace..f7d05e363 100644
--- a/OpenKeychain/src/main/res/raw-sv/help_about.md
+++ b/OpenKeychain/src/main/res/raw-sv/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) är en OpenPGP-implementation till Android.
Licens: GPLv3+
@@ -65,7 +65,7 @@ Licens: GPLv3+
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
* [SafeSlinger's bibliotek för utbyte](https://github.com/SafeSlingerProject/exchange-android) (MIT-licens)
* [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-sv/help_changelog.md b/OpenKeychain/src/main/res/raw-sv/help_changelog.md
index 9edf4979a..f8fa439c6 100644
--- a/OpenKeychain/src/main/res/raw-sv/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-sv/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (NOTERING: Var vänlig och sätt varje mening på sin egen rad, Transifex sätter varje rad i sitt eget fält för översättningar!)
+## 3.9
+
+ * Detection and handling of text data
+ * Performance improvements
+ * UI improvements for Security Token handling
+
+
## 3.8
* Redesigned key editing
diff --git a/OpenKeychain/src/main/res/raw-tr/help_about.md b/OpenKeychain/src/main/res/raw-tr/help_about.md
index 4e8f0270b..fad35b356 100644
--- a/OpenKeychain/src/main/res/raw-tr/help_about.md
+++ b/OpenKeychain/src/main/res/raw-tr/help_about.md
@@ -65,7 +65,7 @@ License: GPLv3+
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-tr/help_changelog.md b/OpenKeychain/src/main/res/raw-tr/help_changelog.md
index b3aab989e..629931b2c 100644
--- a/OpenKeychain/src/main/res/raw-tr/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-tr/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-uk/help_about.md b/OpenKeychain/src/main/res/raw-uk/help_about.md
index 4e8f0270b..fad35b356 100644
--- a/OpenKeychain/src/main/res/raw-uk/help_about.md
+++ b/OpenKeychain/src/main/res/raw-uk/help_about.md
@@ -65,7 +65,7 @@ License: GPLv3+
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-uk/help_changelog.md b/OpenKeychain/src/main/res/raw-uk/help_changelog.md
index b3aab989e..629931b2c 100644
--- a/OpenKeychain/src/main/res/raw-uk/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-uk/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-vi/help_about.md b/OpenKeychain/src/main/res/raw-vi/help_about.md
index 4e8f0270b..fad35b356 100644
--- a/OpenKeychain/src/main/res/raw-vi/help_about.md
+++ b/OpenKeychain/src/main/res/raw-vi/help_about.md
@@ -65,7 +65,7 @@ License: GPLv3+
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-vi/help_changelog.md b/OpenKeychain/src/main/res/raw-vi/help_changelog.md
index b3aab989e..629931b2c 100644
--- a/OpenKeychain/src/main/res/raw-vi/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-vi/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
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-rTW/help_about.md b/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md
index 1d133a37c..1170365ae 100644
--- a/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md
+++ b/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md
@@ -65,7 +65,7 @@
* [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)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 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)
diff --git a/OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md b/OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md
index 14a81bdf1..a62c581bc 100644
--- a/OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
diff --git a/OpenKeychain/src/main/res/raw-zh/advanced.md b/OpenKeychain/src/main/res/raw-zh/advanced.md
index 54a694084..0a1741ce2 100644
--- a/OpenKeychain/src/main/res/raw-zh/advanced.md
+++ b/OpenKeychain/src/main/res/raw-zh/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/help_about.md b/OpenKeychain/src/main/res/raw-zh/help_about.md
index 6f3f841a0..a6ee1740d 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_about.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_about.md
@@ -1,4 +1,4 @@
-[//]: # (注意: 请把每个句子放在其本行中, Transifex把每一行放在它自己的位置!)
+[//]: # (注意:请把每个句子放在单独一行中, Transifex 将把每一行放置在独立的翻译表单内!)
[https://www.openkeychain.org](https://www.openkeychain.org)
@@ -19,7 +19,7 @@
* 'Thialfihar' (APG 开发者)
* Tim Bray
-##临时贡献者
+## 临时贡献者
* Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
@@ -61,11 +61,11 @@
* [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache 许可证 v2)
* [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache 许可证 v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache 许可证 v2)
- * [OkHttp](https://square.github.io/okhttp/) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache 许可证 v2)
+ * [OkHttp](https://square.github.io/okhttp/) (Apache 许可证 v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache 许可证 v2)
* [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT 许可证)
* [Snackbar](https://github.com/nispok/snackbar) (MIT 许可证)
- * [SpongyCastle](https://rtyley.github.io/bouncycastle/) (MIT X11 License)
+ * [BouncyCastle](https://github.com/open-keychain/bouncycastle) (MIT X11 许可证)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache 许可证 v2)
* [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache 许可证 v2)
* [ZXing](https://github.com/zxing/zxing) (Apache 许可证 v2)
diff --git a/OpenKeychain/src/main/res/raw-zh/help_certification.md b/OpenKeychain/src/main/res/raw-zh/help_certification.md
index efac9322b..d9f43875a 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_certification.md
@@ -1,28 +1,28 @@
-[//]: # (注意: 请把每个句子放在其本行中, Transifex把每一行放在它自己的位置!)
+[//]: # (注意:请把每个句子放在单独一行中, Transifex 将把每一行放置在独立的翻译表单内!)
## 密钥确认
-在没有进行确认之前,你无法确保一个密钥与特定的人的密钥是相符的
-确认密钥相符的最简单方式是扫描二维码或者通过NFC交换
-为了确认多于2个人的密钥是相符的, 我们建议使用密钥交换的方法进行确认.
+在进行确认之前,您无法可靠地将一个密钥与特定人物关联起来。
+确认密钥的最简单方式是扫描二维码或者通过NFC进行交换。
+在2人以上的团体内进行密钥确认时,我们建议使用密钥交换的方法。
## 密钥状态
<img src="status_signature_verified_cutout_24dp"/>
-已确认: 你已经通过例如二维码扫描这种方式确认了这个密钥
+已确认:您已经通过某种方式(例如扫描二维码)确认了这个密钥。
<img src="status_signature_unverified_cutout_24dp"/>
-未确认: 这个密钥尚未被确认. 你无法确保这个密钥与指定的人的密钥是相同的.
+未确认:这个密钥尚未被确认。您无法确保这个密钥与特定人物的关联是可靠的。
<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) 标准中的描述是:
-认证的发行者不对妥善检查密钥所有者与密钥所示使用者身份是否相符的情况做出任何表态。
+## 高级信息
+OpenKeychain 中的“密钥确认”行为,是根据 OpenPGP 标准规定,通过创建认证实现的。
+这个认证是一个 [“一般认证(0x10)”](http://tools.ietf.org/html/rfc4880#section-5.2.1) ,标准中的描述是:
+“对于密钥持有者与密钥所示身份之间的关联,认证签发者不对其可靠性做出任何表态。”
-习惯上,认证(更高级的认证,例如“主动认证”(0x13))是有组织的存储在OpenPGP的信任网络中。
-我们的确认密钥模型采用一套相对简单的概念,为了避开上述提到的信任网络中普遍的可用性问题。
-我们假设密钥只被验证到足以随时使用的程度。
-我们也不进行像GnuPG那样的信任签名或导入主观信任数据库。
+习惯上,这些认证(也包括较高的信任等级,例如“正向认证” (0x13) )是以 OpenPGP 的信任网络的形式存在的。
+为了消除信任网络的可用性问题,我们的密钥确认模型采用一套相对简单的概念。
+我们相信密钥的确认过程应该在不妨碍日常使用的情况下尽量可靠。
+我们也不实现像 GnuPG 那样的(可传递)信任签名或信任数据库。
此外,当某密钥含有至少一个被信任密钥所认证过的使用者身份时,它将在密钥列表中被标记为“已确认”。 \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-zh/help_changelog.md b/OpenKeychain/src/main/res/raw-zh/help_changelog.md
index 047ef4858..db352e24e 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_changelog.md
@@ -1,10 +1,17 @@
-[//]: # (备注: 请把每个句子保持在它自己的一行里,Transifex会把每一行放到他应该的位置!)
+[//]: # (注意:请把每个句子放在单独一行中, Transifex 将把每一行放置在独立的翻译表单内!)
+
+## 3.9
+
+ * 检测并处理文本数据
+ * 性能提升
+ * 提升处理安全令牌的 UI 界面
+
## 3.8
- * Redesigned key editing
- * Choose remember time individually when entering passwords
- * Facebook key import
+ * 重新设计了密钥编辑操作
+ * 键入密码时单独选择记忆时间
+ * 导入 Facebook 密钥
## 3.7
@@ -18,7 +25,7 @@
* 加密的备份
* 安全性修复基于外部安全性审查
* YubiKey NEO key 创建向导
- * 支持基本的内部MIME
+ * 支持基本的内部 MIME
* 自动同步密钥
* 试验性功能: 关联密钥到Github, Twitter账户
* 试验性功能: 通过短语确认密钥
@@ -29,7 +36,7 @@
## 3.5
* 密钥删除同时撤销
- * 不安全的加密方法被改进
+ * 改进了不安全的加密方法
* 修复: 第一次向导成功后OpenKeychain不关闭
* API: 第8版
@@ -45,7 +52,7 @@
* 新的解密屏幕
* 一次解密多个文件
- * 更好的YubiKey错误处理机制
+ * 更好的处理 YubiKey 错误
## 3.2
@@ -57,7 +64,7 @@
* 修复同步后丢失联系人
* 需要安卓4
* 重新设计密钥界面
- * 简化加密学的倾向,安全密码更好的选择
+ * 简化加密选项,更好的选择加密算法
* 分离签名,任意选择签名密钥
* 修复:密钥有效却被显示为作废或者到期的问题
* 不受理过期或者作废的子密钥签名
diff --git a/OpenKeychain/src/main/res/raw-zh/help_start.md b/OpenKeychain/src/main/res/raw-zh/help_start.md
index 183c63150..0aed388a3 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_start.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_start.md
@@ -1,16 +1,16 @@
-[//]: # (注意: 请把每个句子放在其本行中, Transifex把每一行放在它自己的位置!)
+[//]: # (注意:请把每个句子放在单独一行中, Transifex 将把每一行放置在独立的翻译表单内!)
## 我怎样才能在 K-9 Mail 中激活 OpenKeychain?
-在K-9 Mail中使用OpenKeychain,请遵循以下步骤:
- 1.打开K-9 Mail并长按你想要使用OpenKeychain的账户。
- 2.选择“账户设置”,滑到最下方,点击“加密”。
- 3.点击“OpenPGP提供者”并在列表中选择OpenKeychain。
+在 K-9 Mail 中使用 OpenKeychain ,请遵循以下步骤:
+ 1. 打开 K-9 Mail 并长按你想要使用 OpenKeychain 的账户。
+ 2. 选择“账户设置”,滑到最下方,点击“加密”。
+ 3. 点击“ OpenPGP 提供者”并在列表中选择 OpenKeychain.
-## 我发现了OpenKeychain的问题!
-请使用 [OpenKeychain问题跟踪系统](https://github.com/openpgp-keychain/openpgp-keychain/issues)提交问题。
+## 我发现了OpenKeychain 的问题!
+请使用 [OpenKeychain 问题跟踪系统](https://github.com/openpgp-keychain/openpgp-keychain/issues) 提交问题。
## 贡献
-如果你想要通过贡献代码来帮助开发OpenKeychain [请按照我们在Github上的一个小指引](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+如果你想要通过贡献代码来帮助开发 OpenKeychain [请按照我们在 Github 上的一个小指引](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
## 翻译
-帮助翻译OpenKeychain!每个人都可以参与。[OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
+帮助翻译 OpenKeychain !每个人都可以参与。 [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/help_changelog.md b/OpenKeychain/src/main/res/raw/help_changelog.md
index ec6a732c1..5babf658a 100644
--- a/OpenKeychain/src/main/res/raw/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw/help_changelog.md
@@ -1,5 +1,12 @@
[//]: # (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
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 bcfb71d0b..78903caa2 100644
--- a/OpenKeychain/src/main/res/values-cs/strings.xml
+++ b/OpenKeychain/src/main/res/values-cs/strings.xml
@@ -9,6 +9,7 @@
<string name="title_decrypt">Rozšifrovat</string>
<string name="title_add_subkey">Přidat podklíč</string>
<string name="title_edit_key">Editovat klíč</string>
+ <string name="title_linked_create">Vyrořit propojenou identitu</string>
<string name="title_preferences">Nastavení</string>
<string name="title_api_registered_apps">Aplikace</string>
<string name="title_key_server_preference">OpenPGP keyservery</string>
@@ -20,33 +21,54 @@
<string name="title_encrypt_to_file">Zašifrovat do souboru</string>
<string name="title_decrypt_to_file">Rozšifrovat do souboru</string>
<string name="title_import_keys">Importovat klíče</string>
+ <string name="title_export_key">Zálohovat klíč</string>
+ <string name="title_export_keys">Zálohovat klíče</string>
<string name="title_key_not_found">Klíč nebyl nalezen</string>
<string name="title_send_key">Nahrát na keyserver</string>
+ <string name="title_backup">Zálohovat klíč</string>
<string name="title_certify_key">Potvrdit klíč</string>
<string name="title_key_details">Detaily klíče</string>
<string name="title_help">Nápověda</string>
<string name="title_log_display">Log</string>
<string name="title_exchange_keys">Vyměnit klíče</string>
+ <string name="title_advanced_key_info">Pokročilé</string>
<string name="title_delete_secret_key">Smazat Váš klíč \'%s\'?</string>
<string name="title_manage_my_keys">Spravovat klíče</string>
<!--section-->
<string name="section_user_ids">Identity</string>
+ <string name="section_security_token">Bezpečnostní token</string>
<string name="section_linked_system_contact">Propojený kontakt v systému</string>
+ <string name="section_keybase_proofs">Keybase.io Proofs</string>
<string name="section_should_you_trust">Důvěřujete tomuto klíči?</string>
<string name="section_proof_details">Doložit ověření</string>
<string name="section_keys">Podklíče</string>
+ <string name="section_cloud_search">Vyhledávání klíče</string>
+ <string name="section_cloud_search_summary">Keyserver, keybase.io</string>
+ <string name="section_passphrase_cache">Hesla a PINy</string>
+ <string name="section_passphrase_cache_summary">Přenášení, uživatelské rozhraní, čas po který si pamatovat</string>
+ <string name="section_proxy_settings">Anonimita na síti</string>
+ <string name="section_proxy_settings_summary">Tor, Nastavení proxy</string>
+ <string name="section_gui">Rozhraní</string>
+ <string name="section_sync_settings">Synchronizace</string>
+ <string name="section_sync_settings_summary">Automatická aktualizace klíče, propojení s kontaktem</string>
+ <string name="section_experimental_features">Experimentální funkce</string>
<string name="section_certify">Potvrdit</string>
<string name="section_actions">Akce</string>
<string name="section_share_key">Klíč</string>
<string name="section_key_server">Keyserver</string>
<string name="section_fingerprint">Otisk</string>
+ <string name="section_phrases">Fráze</string>
<string name="section_encrypt">Zašifrovat</string>
+ <string name="section_decrypt">Dešifrovat / Ověřit</string>
<string name="section_current_expiry">Současná expirace</string>
<string name="section_new_expiry">Nová expirace</string>
<!--button-->
<string name="btn_decrypt_verify_file">Dešifrovat, ověřit a uložit soubor</string>
<string name="btn_encrypt_share_file">Zašifrovat a sdílet soubor</string>
<string name="btn_encrypt_save_file">Zašifrovat a uložit soubor</string>
+ <string name="btn_save_file">Uložit soubor</string>
+ <string name="btn_save">Uložit</string>
+ <string name="btn_view_log">Zobrazit log</string>
<string name="btn_do_not_save">Zrušit</string>
<string name="btn_delete">Smazat</string>
<string name="btn_no_date">Žádná expirace</string>
@@ -56,10 +78,17 @@
<string name="btn_back">Zpět</string>
<string name="btn_no">Ne</string>
<string name="btn_match">Otisky souhlasí</string>
+ <string name="btn_match_phrases">Fráze odpovídá</string>
+ <string name="btn_share_encrypted_signed">Zašifrovat/podepsat a sdílet text</string>
+ <string name="btn_copy_encrypted_signed">Zašifrovat/podepsat a kopírovat text</string>
+ <string name="btn_paste_encrypted_signed">Zašifrovat/podepsat a vložit text</string>
<string name="btn_view_cert_key">Zobrazit klíč certifikátu</string>
<string name="btn_create_key">Vytvořit klíč</string>
<string name="btn_add_files">Přidat soubor(y)</string>
- <string name="btn_copy_decrypted_text">Kopírovat dešifrovaný text</string>
+ <string name="btn_share_decrypted_text">Sdílet</string>
+ <string name="btn_open_with">Otevřít pomocí...</string>
+ <string name="btn_decrypt_clipboard">Načíst ze schránky</string>
+ <string name="btn_decrypt_files">Vybrat vstupní soubor</string>
<string name="btn_encrypt_files">Zašifrovat soubory</string>
<string name="btn_encrypt_text">Zašifrovat text</string>
<string name="btn_add_email">Přidat další emailové adresy</string>
@@ -67,10 +96,16 @@
<string name="btn_add_keyserver">Přidat</string>
<string name="btn_save_default">Uložit jako výchozí</string>
<string name="btn_saved">Uloženo!</string>
+ <string name="btn_not_matching">Neodpovídá</string>
<!--Content Description-->
+ <string name="cd_encrypt_files">Zašifrovat soubory</string>
+ <string name="cd_exchange_keys">Vyměnit klíče</string>
+ <string name="cd_encrypt_text">Zašifrovat text</string>
+ <string name="cd_share_nfc">Sdílet přes NFC</string>
<!--menu-->
<string name="menu_preferences">Nastavení</string>
<string name="menu_help">Nápověda</string>
+ <string name="menu_export_key">Zálohovat klíč</string>
<string name="menu_delete_key">Smazat klíč</string>
<string name="menu_manage_keys">Spravovat klíče</string>
<string name="menu_search">Hledat</string>
@@ -80,6 +115,12 @@
<string name="menu_select_all">Vybrat vše</string>
<string name="menu_export_all_keys">Exportovat všechny klíče</string>
<string name="menu_update_all_keys">Aktualizovat všechny klíče</string>
+ <string name="menu_advanced">Pokročilé</string>
+ <string name="menu_certify_fingerprint">Ověřit pomocí otisku prstu</string>
+ <string name="menu_certify_fingerprint_phrases">Ověřit pomocí frází</string>
+ <string name="menu_share_log">Sdílet log</string>
+ <string name="menu_change_password">Změnit heslo</string>
+ <string name="menu_keyserver_add">Přidat</string>
<!--label-->
<string name="label_message">Text</string>
<string name="label_file">Soubor</string>
@@ -87,6 +128,7 @@
<string name="label_file_colon">Soubor:</string>
<string name="label_no_passphrase">Žádné heslo</string>
<string name="label_passphrase">Heslo</string>
+ <string name="label_pin">PIN</string>
<string name="label_unlock">Odemykám...</string>
<string name="label_passphrase_again">Zopakovat heslo</string>
<string name="label_show_passphrase">Zobrazit heslo</string>
@@ -95,11 +137,15 @@
<string name="label_file_ascii_armor">Povolit ASCII armor</string>
<string name="label_write_version_header">Dát ostatním vědět, že používáte OpenKeychain</string>
<string name="label_write_version_header_summary">Zapisovat \'OpenKeychain v2.7\' do OpenPGP podpisů, šifrovaného textu a exportovaných klíčů</string>
+ <string name="label_use_num_keypad_for_security_token_pin">Použít numerickou klávesnici pro PIN bezpečnostního tokenu</string>
+ <string name="label_asymmetric_from">Podepsat pomocí:</string>
+ <string name="label_to">Zašifrovat do</string>
<string name="label_delete_after_encryption">Smazat soubory po zašifrování</string>
<string name="label_delete_after_decryption">Smazat po rozšifrování</string>
<string name="label_encryption_algorithm">Šifrovací algoritmus</string>
<string name="label_hash_algorithm">Hashovací algoritmus</string>
<string name="label_symmetric">Zašifrovat heslem</string>
+ <string name="label_passphrase_cache_subs">Zapamatovat hesla pomocí subklíče</string>
<string name="label_message_compression">Komprimovat text</string>
<string name="label_file_compression">Komprimovat soubor</string>
<string name="label_keyservers">Vyberte OpenPGP keyservery</string>
@@ -114,22 +160,73 @@
<string name="label_name">Jméno</string>
<string name="label_comment">Komentář</string>
<string name="label_email">Email</string>
+ <string name="label_send_key">Synchronizovat s Internetem</string>
<string name="label_fingerprint">Otisk</string>
<string name="expiry_date_dialog_title">Nastavit datum expirace</string>
+ <string name="label_keyservers_title">Keyservery</string>
+ <string name="label_keyserver_settings_hint">Pro změnu řazení přetáhněte, pro editaci/smazání klepněte</string>
+ <string name="label_selected_keyserver_title">Vyberte keyserver</string>
<string name="label_preferred">upřednostněno</string>
<string name="label_enable_compression">Zapnout kompresi</string>
<string name="label_encrypt_filenames">Zašifrovat jména souborů</string>
<string name="label_hidden_recipients">Skrýt příjemce</string>
+ <string name="label_verify_keyserver_connection">Otestovat připojení</string>
+ <string name="label_only_trusted_keyserver">Pouze důvěryhodný keyserver</string>
+ <string name="label_enter_keyserver_url">URL</string>
+ <string name="label_keyserver_dialog_delete">Smazat keyserver</string>
+ <string name="label_theme">Téma vzhledu</string>
<string name="pref_keyserver">OpenPGP keyserver</string>
<string name="pref_keyserver_summary">Vyhledat klíče na vybraném OpenPGP keyserveru (protokol HKP)</string>
<string name="pref_keybase">keybase.io</string>
<string name="pref_keybase_summary">Hledat klíče na keybase.io</string>
+ <string name="pref_facebook">Facebook</string>
+ <string name="pref_facebook_summary">Vyhledat klíče na Facebooku pomocí uživatelského jména</string>
+ <string name="label_sync_settings_keyserver_title">Automaticky aktualizovat klíč</string>
+ <string name="label_sync_settings_keyserver_summary_on">Klíče jsou aktualizovány z preferovaného keyserveru každé tři dny</string>
+ <string name="label_sync_settings_keyserver_summary_off">Klíče nejsou automaticky aktualizovány</string>
+ <string name="label_sync_settings_contacts_title">Propojit klíče s kontakty</string>
+ <string name="label_sync_settings_contacts_summary_on">Protojit klíče s kontakty na základě jmen a emailových adres. Odehrává se to kompletně offline na vašem zařízení.</string>
+ <string name="label_sync_settings_contacts_summary_off">Nové klíče nebudou propoeny ke kontaktům</string>
<!--label shown in Android settings under the OpenKeychain account-->
+ <string name="keyserver_sync_settings_title">Automaticky aktualizovat klíč</string>
+ <string name="label_experimental_settings_desc_title">Varovnání</string>
+ <string name="label_experimental_settings_desc_summary">Tyto funkce nejsou ještě dokončené nebo neprošli uživatelským testováním/bezpečnostním auditem. Prosím nepoužívejte je v situacích kdy na nich závisí vaše zabezpečení a nehlašte problémy s nimi spojené, pokud na ně narazíte.</string>
+ <string name="label_experimental_settings_word_confirm_title">Ověření pomocí frází</string>
+ <string name="label_experimental_settings_word_confirm_summary">Ověřit klíč pomocí frází namísto hexadecimálního fingerprintu</string>
+ <string name="label_experimental_settings_linked_identities_title">Propojené identity</string>
+ <string name="label_experimental_settings_linked_identities_summary">Propojit klíče s Twitterem, GitHubem, webem nebo DNS (podobně jako keybase.io ale decentralizované)</string>
+ <string name="label_experimental_settings_keybase_title">Keybase.io Proofs</string>
+ <string name="label_experimental_settings_keybase_summary">Kontaktovat keybase.io pro proofs klíče a ukázat je pokaždé když je klíč zobrazen</string>
+ <string name="label_experimental_settings_theme_summary">(Ikony a mnoho obrazovek ještě nejsou uzpůsobené pro temné téma vzhledu)</string>
<!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Zapnout Tor</string>
+ <string name="pref_proxy_tor_summary">Vyžaduje instalovaný Orbot </string>
+ <string name="pref_proxy_normal_title">Zapnout jinou proxy</string>
+ <string name="pref_proxy_host_title">Proxy server</string>
+ <string name="pref_proxy_host_err_invalid">Proxy server nemůže zůstat prázdný</string>
+ <string name="pref_proxy_port_title">Proxy port</string>
+ <string name="pref_proxy_port_err_invalid">Vložen neplatné číslo portu</string>
+ <string name="pref_proxy_type_title">Typ 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">Nepoužívat Tor</string>
<!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Nainstalovat Orbot abyste mohli použít Tor?</string>
+ <string name="orbot_install_dialog_install">Instalovat</string>
+ <string name="orbot_install_dialog_content">Musíte mít nainstalovaný a aktivovaný Orbot abyste mohli vést provoz skrz něj. Chcete ho nainstalovat?</string>
+ <string name="orbot_install_dialog_cancel">Zrušit</string>
+ <string name="orbot_install_dialog_ignore_tor">Nepoužívat Tor</string>
<!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Spustit Orbot?</string>
+ <string name="orbot_start_dialog_content">Nezdá se že by Orbot běžel. Chcete ho spustit a připojit se do Toru?</string>
+ <string name="orbot_start_btn">Spustit Orbot</string>
+ <string name="orbot_start_dialog_start">Spustit Orbot</string>
+ <string name="orbot_start_dialog_cancel">Zrušit</string>
+ <string name="orbot_start_dialog_ignore_tor">Nepoužívat Tor</string>
+ <string name="user_id_no_name"><![CDATA[<nepojmenový>]]></string>
+ <string name="none"><![CDATA[<žádný>]]></string>
<plurals name="n_keys">
<item quantity="one">1 klíč</item>
<item quantity="few">%d klíče</item>
@@ -155,35 +252,42 @@
<string name="choice_4hours">4 hodiny</string>
<string name="choice_8hours">8 hodin</string>
<string name="choice_forever">navždy</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">Vybrat klíč</string>
<string name="filemanager_title_open">Otevřít...</string>
<string name="error">Chyba</string>
<string name="error_message">Chyba: %s</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>
+ <string name="theme_dark">Temný</string>
+ <string name="theme_light">Světlý</string>
<!--sentences-->
<string name="wrong_passphrase">Chybné heslo.</string>
<string name="no_filemanager_installed">Není nainstalován žádný compatibilní správce souborů.</string>
<string name="passphrases_do_not_match">Hesla se neschodují.</string>
<string name="passphrase_must_not_be_empty">Zadejte heslo.</string>
+ <string name="passphrase_for_symmetric_encryption">Zadejte heslo</string>
+ <string name="passphrase_for_backup">Zadejte záložní kód</string>
<string name="passphrase_for">Zadejte heslo pro \'%s\'</string>
<string name="pin_for">Zadejte PIN pro \'%s\'</string>
+ <string name="security_token_pin_for">Zadejte PIN pro přístup k bezpečnostnímu tokenu pro \'%s\'</string>
+ <string name="security_token_nfc_text">Podržte bezpečnostní token u NFC značky na zadní straně vašeho přístroje.</string>
+ <string name="security_token_nfc_wait">Podržte bezpečnostní token u zadní strany!</string>
+ <string name="security_token_nfc_finished">Nyní odeberte bezpečnostní token.</string>
+ <string name="security_token_nfc_try_again_text">Nyní odeberte bezpečnostní token a zmáčkněte ZKUSIT ZNOVU.</string>
<string name="file_delete_confirmation_title">Smazat původní soubory?</string>
<string name="file_delete_confirmation">Následjící soubory budou vymazány:%s</string>
<string name="file_delete_successful">%1$d z %2$d souborů bylo vymazáno.%3$s</string>
+ <string name="no_file_selected">Nebyl vybrán žádný soubor.</string>
<string name="encrypt_sign_successful">Úspěšně podepsáno a/nebo zašifrováno.</string>
<string name="encrypt_sign_clipboard_successful">Úspěšně podepsání a/nebo zašifrováno do schránky.</string>
<string name="select_encryption_key">Vyberte alespoň jeden šifrovací klíč.</string>
+ <string name="error_no_encryption_or_signature_key">Vyberte alespoň jeden šifrovací nebo podpisový klíč.</string>
<string name="specify_file_to_encrypt_to">Prosím specifikujte do kterého souboru zašifrovat.\nVAROVÁNÍ: Pokud soubor již existuje, bude přepsán.</string>
<string name="specify_file_to_decrypt_to">Prosím specifikujte do kterého souboru rozšifrovat.\nVAROVÁNÍ: Pokud soubor již existuje, bude přepsán.</string>
+ <string name="specify_backup_dest">Bude provedena záloha vyjma vašich klíčů, prosím specifikujte cílový sobor.\nVAROVÁNÍ: Soubor bude přepsán pokud existuje!</string>
+ <string name="specify_backup_dest_single">Tento klíč bude sdílen, prosím specifikujte cílový soubor.\nVAROVÁNÍ: Pokud soubor existuje, bude přepsán!</string>
+ <string name="specify_backup_dest_secret_single">Úplná záloha vašeho klíče bude provedena, prosím specifikujte cílový soubor.\nVAROVÁNÍ: Pokud soubor již existuje, bude přepsán.</string>
+ <string name="specify_backup_dest_secret">Bude provedena úplná záloha všech vašich klíčů, prosím specifikujte cílový soubor.\nVAROVÁNÍ: Pokud soubor již existuje, bude přepsán.</string>
<string name="key_deletion_confirmation_multi">Opravdu chcete smazat všechny vybrané soubory?</string>
+ <string name="secret_key_deletion_confirmation">Po smazání nebudete moci dešifrovat zprávy/soubory zašifrované tímto klíčem a příjdete o všechna potvrzení k nimž jste tento klíč použili.</string>
<string name="public_key_deletetion_confirmation">Smazat klíč \'%s\'?</string>
<string name="also_export_secret_keys">Zárověň exportovat tajný klíč</string>
<string name="reinstall_openkeychain">Narazili jste na známou chybu v Androidu. Přeinstalujte prosím OpenKeychain pokud chcete své propojit kontakty s klíči.</string>
@@ -198,6 +302,7 @@
<string name="fingerprint_copied_to_clipboard">Fingerprint byl zkopírován do schránky!</string>
<string name="select_key_to_certify">Prosím vyberte klíč, který bude použit k potvrzení!</string>
<string name="text_copied_to_clipboard">Text byl zkopírován do schránky!</string>
+ <string name="how_to_import">Jak toto importovat na PC?</string>
<!--errors
no punctuation, all lowercase,
they will be put after "error_message", e.g. "Error: file not found"-->
@@ -318,6 +423,7 @@
<string name="help_about_version">Verze:</string>
<!--Import-->
<string name="import_tab_keyserver">Keyserver</string>
+ <string name="import_tab_cloud">Vyhledávání klíče</string>
<string name="import_tab_direct">Soubor/schránka</string>
<string name="import_tab_qr_code">QR kód/NFC</string>
<string name="import_import">Importovat vybrané klíče</string>
@@ -334,18 +440,60 @@
<string name="import_error_nothing">Nic k importu</string>
<string name="import_error_nothing_cancelled">Import zrušen.</string>
<!--Delete result toast-->
+ <plurals name="delete_ok_but_fail_1">
+ <item quantity="one">Úspěšně smazán jeden klíč</item>
+ <item quantity="few">Úspěšně smazáno %1$d klíče</item>
+ <item quantity="other">Úspěšně smazáno %1$d klíčů</item>
+ </plurals>
+ <plurals name="delete_ok_but_fail_2">
+ <item quantity="one">, ale selhalo smazání jednoho klíče%2$s.</item>
+ <item quantity="few">, ale selhalo smazání %1$d klíčů%2$s.</item>
+ <item quantity="other">, ale selhalo smazání %1$d klíčů%2$s.</item>
+ </plurals>
+ <plurals name="delete_ok">
+ <item quantity="one">Úspěšně smazáno klíče%2$s.</item>
+ <item quantity="few">Úspěšně smazáno %1$d klíčů%2$s.</item>
+ <item quantity="other">Úspěšně smazáno %1$d klíčů%2$s.</item>
+ </plurals>
+ <plurals name="delete_fail">
+ <item quantity="one">Chyba mazání jednoho klíče%2$s.</item>
+ <item quantity="few">Chyba smazání %1$d klíčů.</item>
+ <item quantity="other">Chyba smazání %1$d klíčů.</item>
+ </plurals>
<string name="delete_nothing">Nic ke smazání.</string>
<string name="delete_cancelled">Operace smazání zrušena.</string>
<!--Revoke result toast (snackbar)-->
+ <string name="revoke_ok">Klíč úspěšně zneplatněn.</string>
+ <string name="revoke_fail">Chyba zneplatnění klíče!</string>
+ <string name="revoke_nothing">Nic k zneplatnění</string>
+ <string name="revoke_cancelled">Operace zneplatnění zrušena.</string>
<!--Certify result toast-->
+ <plurals name="certify_keys_ok">
+ <item quantity="one">Úspěšně potvrzen klíč%2$s.</item>
+ <item quantity="few">Úspěšně potvrzeny %1$d klíče%2$s.</item>
+ <item quantity="other">Úspěšně potvrzeno %1$d klíčů%2$s.</item>
+ </plurals>
+ <plurals name="certify_keys_with_errors">
+ <item quantity="one">Certifikace selhala!</item>
+ <item quantity="few">Certifikace selhala pro %d klíče!</item>
+ <item quantity="other">Certifikace selhala pro %d klíčů!</item>
+ </plurals>
+ <plurals name="certify_error">
+ <item quantity="one">Certifikace selhala!</item>
+ <item quantity="few">Certifikace pro %d klíče selhala!</item>
+ <item quantity="other">Certifikace pro %d klíčů selhala!</item>
+ </plurals>
<!--Intent labels-->
<string name="intent_decrypt_file">Dešifrovat soubor pomocí OpenKeychain</string>
<string name="intent_import_key">Importovat kíč pomocí OpenKeychain</string>
<string name="intent_send_encrypt">Zašifrovat pomocí OpenKeychain</string>
<string name="intent_send_decrypt">Dešifrovat pomocí OpenKeychain</string>
<!--Remote API-->
+ <string name="api_settings_show_info">Zobrazit pokročilé informace</string>
+ <string name="api_settings_hide_info">Schovat pokročilá informace</string>
<string name="api_settings_no_key">Žádný klíč nebyl vybrán</string>
<string name="api_settings_select_key">Vybrat klíč</string>
+ <string name="api_settings_create_key">Vytvořit nový klíč</string>
<string name="api_settings_save">Uložit</string>
<string name="api_settings_save_msg">Účet byl uložen</string>
<string name="api_settings_cancel">Zrušit</string>
@@ -353,12 +501,21 @@
<string name="api_settings_start">Spustit aplikaci</string>
<string name="api_settings_delete_account">Smazat účet</string>
<string name="api_settings_package_name">Jméno balíčku</string>
+ <string name="api_settings_package_certificate">SHA-256 z certifikátu balíčku</string>
+ <string name="api_settings_accounts">Účty (staré API)</string>
+ <string name="api_settings_advanced">Pokročilé</string>
+ <string name="api_settings_allowed_keys">Povolené klíče</string>
<string name="api_settings_settings">Nastavení</string>
<string name="api_settings_key">Klíč účtu:</string>
<string name="api_settings_accounts_empty">Žádné účty nejsou specifikovány pro tuto appku.</string>
+ <string name="api_create_account_text">Pro tento účet není nastaven žádný klíč. Vyberte prosím jeden z existujících klíčů nebo vytvořte nový.\nAplikace mohou rošifrovat/podepisovat pouze klíči, které jsou zde vybrány!</string>
+ <string name="api_update_account_text">Klíč uložen pro tento účet byl smazán. Vyberte prosím jiný!\nAplikace mohou rošifrovat/podepisovat pouze klíči, které jsou zde vybrány!</string>
+ <string name="api_register_text">Zobrazená appka chce zašifrovat/rozšifrovat zprávu a podepsat jí vaším jménem.\nPovolit přístup?\n\nVAROVÁNÍ: Pokud nevíte proč se tato zpráva oběvila, nepovolujte přístup! Zneplatnint přístup můžete také pomocí obrazovky \'Aplikace\'.</string>
<string name="api_register_allow">Povolit přístup</string>
<string name="api_register_disallow">Zamítnout přístup</string>
<string name="api_register_error_select_key">Prosím vyberte klíč!</string>
+ <string name="api_select_pub_keys_missing_text">Pro tyto emailové adresy nebyl nalezen žádný klíč:</string>
+ <string name="api_select_pub_keys_dublicates_text">Pro tyto emailové adresy existuje více jak jeden klíč:</string>
<string name="api_select_pub_keys_text">Zkontrolujte prosím seznam příjemců!</string>
<string name="api_select_pub_keys_text_no_user_ids">Vyberte prosím příjemce!</string>
<string name="api_error_wrong_signature">Selhala kontrola podpisu! Instalovali jste tuto aplikaci z jiného zdroje? Pokud jste si jistí, že toto není útok, zneplatněte registraci této aplikace v OpenKeychain a poté ji znovu zaregistrujte.</string>
@@ -368,8 +525,18 @@
<string name="share_qr_code_dialog_title">Sdílet pomocí QR kódu</string>
<string name="share_nfc_dialog">Sdílet pomocí NFC</string>
<!--retry upload dialog-->
+ <string name="retry_up_dialog_title">Nahrávání selhalo</string>
+ <string name="retry_up_dialog_message">Nahrávání selhalo. Chcete to zkušit znovu?</string>
+ <string name="retry_up_dialog_btn_reupload">Zkusit znovu</string>
+ <string name="retry_up_dialog_btn_cancel">Zrušit operaci</string>
<!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_message">Pokud nechcete nadáe používat tento klíč, měl by být zevokován a nahrán. Vyberte \'POUZE SMAZAT\' pokud si přejete smazat klíč z OpenKeychain ale chcete ho i nadále používat někde jinde.</string>
+ <string name="del_rev_dialog_title">Zneplatnit/Smazat klíč \'%s\'</string>
+ <string name="del_rev_dialog_btn_revoke">Zneplatnit a nahrát</string>
+ <string name="del_rev_dialog_btn_delete">Pouze smazat</string>
<!--Delete Or Revoke Dialog spinner-->
+ <string name="del_rev_dialog_choice_delete">Pouze smazat</string>
+ <string name="del_rev_dialog_choice_rev_upload">Zneplatnit a Nahrát</string>
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 kíč vybrán</item>
@@ -378,7 +545,12 @@
</plurals>
<string name="key_list_empty_text1">Žádný klíč nenalezen!</string>
<string name="key_list_filter_show_all">Zobrazit všechny klíče</string>
+ <string name="key_list_filter_show_certified">Zobrazit pouze potvrzené klíče</string>
+ <string name="key_list_fab_qr_code">Naskenovat QR kód</string>
+ <string name="key_list_fab_search">Vyhledávání klíče</string>
+ <string name="key_list_fab_import">Importovat ze souboru</string>
<!--Key view-->
+ <string name="key_view_action_edit">Editovat</string>
<string name="key_view_action_encrypt">Zašifrovat text</string>
<string name="key_view_action_encrypt_files">soubory</string>
<string name="key_view_action_certify">Potvrdit klíč</string>
@@ -387,22 +559,51 @@
<string name="key_view_action_share_nfc">Sdílet přes NFC</string>
<string name="key_view_action_upload">Nahrát na keyserver</string>
<string name="key_view_tab_main">Hlavní info</string>
+ <string name="key_view_tab_start">Začít</string>
<string name="key_view_tab_share">Sdílet</string>
<string name="key_view_tab_keys">Podklíče</string>
<string name="key_view_tab_certs">Certifikáty</string>
<string name="key_view_tab_keybase">Keybase.io</string>
<string name="user_id_info_revoked_title">Zneplatněno</string>
<string name="user_id_info_revoked_text">Tato identity byla zneplatněna vlastníkem klíče. Klíč již není platný.</string>
+ <string name="user_id_info_certified_title">Potvrzeno</string>
+ <string name="user_id_info_certified_text">Tato identita byla potvrzena vámi.</string>
+ <string name="user_id_info_uncertified_title">Nepotvrzeno</string>
+ <string name="user_id_info_uncertified_text">Tato identita zatím nebyla potvrzena. Nemůžete si být jisti zda je identita odpovídá určité osobě.</string>
<string name="user_id_info_invalid_title">Neplatná</string>
<string name="user_id_info_invalid_text">S touto identitou je něco v nepořádku!</string>
<!--Key trust-->
+ <string name="key_trust_no_cloud_evidence">Žádný důkaz o důvěryhodnosti klíče z internetu.</string>
<string name="key_trust_start_cloud_search">Vyhledat</string>
<string name="key_trust_results_prefix">Keybase.io nabízí “důkazy” které tvrdí, že vlastníkem tohoto klíče je: </string>
<!--keybase proof stuff-->
+ <string name="keybase_narrative_twitter">Postuje na Twitter jako %s</string>
+ <string name="keybase_narrative_github">Je znám na GitHubu jako %s</string>
+ <string name="keybase_narrative_dns">Spravuje doménové jmén(o/a) %s</string>
+ <string name="keybase_narrative_web_site">Postuje na webovou stránku(y) %s</string>
+ <string name="keybase_narrative_reddit">Postuje na Reddit jako %s</string>
+ <string name="keybase_narrative_coinbase">Je znám na Coinbase jako %s</string>
+ <string name="keybase_narrative_hackernews">Postuje na Hacker News jako %s</string>
+ <string name="keybase_narrative_unknown">Neznámý typ důkazu %s</string>
+ <string name="keybase_proof_failure">Naneštěstí tento důkaz nemůže být ověřen.</string>
+ <string name="keybase_unknown_proof_failure">Nerozpoznatelný problém s ověřováním důkazu</string>
+ <string name="keybase_problem_fetching_evidence">Problém s důkazem</string>
+ <string name="keybase_key_mismatch">Otisk klíče nesouhlasí s tím v důkazu</string>
+ <string name="keybase_dns_query_failure">Stažení záznamu DNS TXT selhalo</string>
+ <string name="keybase_no_prover_found">Nenalezen zůsob ověření pro</string>
+ <string name="keybase_message_payload_mismatch">Rošifrovaný záznam důkazu nesouhlasí s očekávanou hodnotou</string>
+ <string name="keybase_message_fetching_data">Stahuji důkaz</string>
+ <string name="keybase_proof_succeeded">Tento důkaz byl ověřen!</string>
<string name="keybase_a_post">Příspěvek</string>
+ <string name="keybase_fetched_from">stažen z</string>
+ <string name="keybase_for_the_domain">pro doménu</string>
+ <string name="keybase_contained_signature">obsahuje zprávu, která mohla být vytvořena pouze vlastníkem tohoto klíče.</string>
<string name="keybase_twitter_proof">tweet</string>
+ <string name="keybase_dns_proof">Záznam DNS TXT</string>
<string name="keybase_web_site_proof">textový soubor</string>
+ <string name="keybase_github_proof">Gist</string>
<string name="keybase_reddit_proof">soubor JSON</string>
+ <string name="keybase_reddit_attribution">Reddit připisuje</string>
<string name="keybase_verify">Ověřit</string>
<!--Edit key-->
<string name="edit_key_action_change_passphrase">Změnit heslo</string>
@@ -418,11 +619,20 @@
</string-array>
<string name="edit_key_edit_user_id_revoked">Tato identity byla zneplatněna. Toto není možné vzít zpět.</string>
<string name="edit_key_edit_subkey_title">Vyberte akci!</string>
+ <string-array name="edit_key_edit_subkey">
+ <item>Změnit expiraci</item>
+ <item>Zneplatnit podklíč</item>
+ <item>Svléknout podklíč</item>
+ <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>
+ <string name="edit_key_error_bad_security_token_size">Dékla klíče není bezpečnostním tokenem podporována!</string>
+ <string name="edit_key_error_bad_security_token_stripped">Nemohu přesunout klíč do bezpečnostního tokenu (buď je holý nebo označen jako \'divert-to-card\')!</string>
<!--Create key-->
+ <string name="create_key_upload">Synchronizovat s Internetem</string>
<string name="create_key_empty">Toto pole je vyžadováno</string>
<string name="create_key_passphrases_not_equal">Hesla se neschodují.</string>
<string name="create_key_final_text">Zadali jste následující identitu:</string>
@@ -437,6 +647,7 @@
<string name="create_key_add_email_text">Další emailové adresy jsou přiřazeny k tomuto klíči a mohou být požity k zabezpečené komunikaci.</string>
<string name="create_key_email_already_exists_text">Emailová adresa již byla přidána.</string>
<string name="create_key_email_invalid_email">Formát emailové adresy není platný.</string>
+ <string name="create_key_yubi_key_pin">PIN</string>
<!--View key-->
<string name="view_key_revoked">Zneplatněn: Klíč nesmí být dále použit!</string>
<string name="view_key_expired">Vypršel: Kontakt musí rozšířit platnost klíče!</string>
@@ -447,8 +658,14 @@
<string name="view_key_fragment_no_system_contact">&lt;žádný&gt;</string>
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Přidat keyserver</string>
+ <string name="edit_keyserver_dialog_title">Editovat keyserver</string>
+ <string name="add_keyserver_connection_verified">Spojení ověřeno!</string>
<string name="add_keyserver_without_verification">Keyserver přidán bez verifikace.</string>
<string name="add_keyserver_invalid_url">Neplatná URL!</string>
+ <string name="add_keyserver_keyserver_not_trusted">Keyserver není jedním z důvěryhodných (žádný pinned certifikát k dispozici)!</string>
+ <string name="add_keyserver_connection_failed">Selhalo spojení s keyserverem. Ověřte prosím URL a vaše připojení na Internet.</string>
+ <string name="keyserver_preference_deleted">%s smazán</string>
+ <string name="keyserver_preference_cannot_delete_last">Není možné smazat poslední keyserver. Alespoň jeden je vyžadován!</string>
<!--Navigation Drawer-->
<string name="nav_keys">Klíče</string>
<string name="nav_encrypt_decrypt">Zašifrovat/Dešifrovat</string>
@@ -456,6 +673,7 @@
<string name="drawer_open">Otevřít navigační panel</string>
<string name="drawer_close">Zavří navigační panel</string>
<string name="my_keys">Moje Klíče</string>
+ <string name="nav_backup">Záloha/Obnova</string>
<!--hints-->
<string name="encrypt_content_edit_text_hint">Napsat text</string>
<!--certs-->
@@ -505,6 +723,7 @@
<string name="msg_ip_master_flags_xxxa">Hlavní příznaky: přihlášení</string>
<string name="msg_ip_master_flags_xxxx">Hlavní příznaky: žádné</string>
<string name="msg_ip_merge_public">Slučuji importovaná data do existující veřejné klíčenky</string>
+ <string name="msg_ip_merge_secret">Zařazuji importovaná data do existující zabezpečené klíčenky</string>
<string name="msg_ip_subkey">Zpracovávám podklíč %s</string>
<string name="msg_ip_subkey_expired">Podklíč vypršel %s</string>
<string name="msg_ip_subkey_expires">Podklíč vypršel %s</string>
@@ -535,6 +754,17 @@
<string name="msg_ip_uid_cert_new">Certifikát je novější, nahrazuji předchozí.</string>
<string name="msg_ip_uid_cert_good">Nalezen platný certifikát od %1$s</string>
<string name="msg_ip_uid_cert_good_revoke">Nalezena platné zneplatnění certifikátu od %1$s</string>
+ <plurals name="msg_ip_uid_certs_unknown">
+ <item quantity="one">Ignoruji certifikát vydaný neznámým veřejným klíčem.</item>
+ <item quantity="few">Ignoruji %s certifikátů vydaných neznámým veřejnými klíči.</item>
+ <item quantity="other">Ignoruji %s certifikátů vydaných neznámým veřejnými klíči.</item>
+ </plurals>
+ <string name="msg_ip_uid_classifying_zero">Klasifikuji uživatelská ID (žádné důvěryhodné klíče k dispozici)</string>
+ <plurals name="msg_ip_uid_classifying">
+ <item quantity="one">Klasifikuji uživatelské ID (používám jeden důvěryhodný klíč)</item>
+ <item quantity="few">Klasifikuji uživatelská ID (používám %s důvěryhodné klíče)</item>
+ <item quantity="other">Klasifikuji uživatelská ID (používám %s důvěryhodných klíčů)</item>
+ </plurals>
<string name="msg_ip_uid_reorder">Přeskládávám uživatelské jména</string>
<string name="msg_ip_uid_processing">Zpracovávám uživatelské jméno %s</string>
<string name="msg_ip_uid_revoked">Uživatelské jméno zneplatněno</string>
@@ -547,6 +777,12 @@
<string name="msg_ip_uat_cert_new">Certifikát je novější, nahrazuji předchozí.</string>
<string name="msg_ip_uat_cert_good">Nalezen platný certifikát od %1$s</string>
<string name="msg_ip_uat_cert_good_revoke">Nalezena platné zneplatnění certifikátu od %1$s</string>
+ <plurals name="msg_ip_uat_certs_unknown">
+ <item quantity="one">Ignoruji jeden certifikát vydaný neznámým veřejným klíčem</item>
+ <item quantity="few">Ignoruji %s certifikáty vydané neznámými veřejnými klíči</item>
+ <item quantity="other">Ignoruji %s certifikáty vydané neznámými veřejnými klíči</item>
+ </plurals>
+ <string name="msg_ip_uat_classifying">Třídím uživatelské atributy</string>
<string name="msg_ip_uat_revoked">Atribut uživatele je zneplatněn</string>
<string name="msg_is_bad_type_public">Pokus o import veřejné klíčenky jako tajné. Toto je bug, prosím nahlašte ho!</string>
<string name="msg_is_bad_type_uncanon">Pokus o importování klíčenky bez kanonizace. Toto je bug, prosím vyplňte hlášení!</string>
@@ -556,14 +792,20 @@
<string name="msg_is_importing_subkeys">Zpracovávám tajné podklíče</string>
<string name="msg_is_error_io_exc">Chyba kódování klíčenky</string>
<string name="msg_is_merge_public">Slučuji importovaná data do existující veřejné klíčenky</string>
+ <string name="msg_is_merge_secret">Slučuji importovaná data do existující zabezpečené klíčenky</string>
+ <string name="msg_is_merge_special">Slučuji data vlastních certifikátů z veřejné klíčenky</string>
<string name="msg_is_pubring_generate">Vytvářím veřejnou klíčenku z tajné</string>
<string name="msg_is_subkey_nonexistent">Podklíč %s není k dispozici v tajném klíči</string>
<string name="msg_is_subkey_ok">Označen tajný podklíč %s jako že je k dispozici</string>
<string name="msg_is_subkey_empty">Označen tajný podklíč %s jako k dispozici s prázným heslem</string>
<string name="msg_is_subkey_pin">Označen tajný podklíč %s jako že je k dispozici s PINem</string>
+ <string name="msg_is_subkey_stripped">Tajný podklíč %s označen jako holý</string>
+ <string name="msg_is_subkey_divert">Tajný podklíč %s označen jako \'divert-to-card\'</string>
<string name="msg_is_success_identical">Klíčenka neobsahuje žádná nová data, není co provést</string>
<string name="msg_is_success">Úspěšně importován tajný klíč</string>
<!--Keyring Canonicalization log entries-->
+ <string name="msg_kc_public">Kanonizuji veřejnou klíčenku %s</string>
+ <string name="msg_kc_secret">Kanonizuji tajnou klíčenku %s</string>
<string name="msg_kc_error_v3">Toto je klíč OpenPGP verze 3, který již naní podporován!</string>
<string name="msg_kc_error_no_uid">Klíčenka neobsahuje žádná platná uživatelská jména!</string>
<string name="msg_kc_error_master_algo">Hlavní klíč používá neznámý (%s) algoritmus!</string>
@@ -577,18 +819,66 @@
<string name="msg_kc_master_bad">Odebírám špatný hlavní klíč certifikátu</string>
<string name="msg_kc_master_local">Odebírám hlavní klíč certifikátu s příznakem \'lokální\'</string>
<string name="msg_kc_revoke_dup">Odebírám redundantní zneplatňovací certifikát klíčenky</string>
+ <string name="msg_kc_notation_dup">Odebírám nadbytečný certifikát</string>
+ <string name="msg_kc_notation_empty">Odebírám prázdný natation certifikát</string>
+ <string name="msg_kc_sub">Zpracovávám podklíč %s</string>
+ <string name="msg_kc_sub_bad">Odebírám neplatné napojení podklíče certifikátu</string>
+ <string name="msg_kc_sub_bad_err">Odebírám spatné napojení podklíče certifikátu</string>
+ <string name="msg_kc_sub_bad_local">Odebírám napojení podklíče certifikátu s příznakem \'lokální\'</string>
+ <string name="msg_kc_sub_bad_keyid">Napojení podklíče na id vydavatele nesouhlasí</string>
+ <string name="msg_kc_sub_bad_time">Odebírám napojení podklíče s časovou značkou v budoucnu</string>
+ <string name="msg_kc_sub_bad_time_early">Napojení podklíče certifikátu má dřívější čas než jeho klíč!</string>
+ <string name="msg_kc_sub_bad_type">Neznámý typ certifikátu podklíče: %s</string>
+ <string name="msg_kc_sub_dup">Odebírám nadbitečné napojení podklíče certifikátu</string>
+ <string name="msg_kc_sub_primary_bad">Odebírám napojení podklíče certifikátu kůli neplatnému napojení hlavního certifikátu</string>
+ <string name="msg_kc_sub_primary_bad_err">Odebírám napojení podklíče certifikátu kůli čpatnému napojení hlavního certifikátu</string>
+ <string name="msg_kc_sub_primary_none">Odebírám napojení podklíče certifikátu kůli chybějícímu napojení hlavního certifikátu</string>
+ <string name="msg_kc_sub_no_cert">Pro %s nebyl nalezen žádný platný certifikát, odebírám z klíčenky</string>
+ <string name="msg_kc_sub_revoke_bad_err">Odebírám špatný zneplatňovací podklíč certifikátu</string>
+ <string name="msg_kc_sub_revoke_bad">Odebírám špatný zneplatňovací podklíč certifikátu</string>
+ <string name="msg_kc_sub_revoke_dup">Odebírám redundantní zneplatňovací podklíč certifikátu</string>
+ <string name="msg_kc_sub_unknown_algo">Podklíč používá neznámý algoritmus, neimportuji...</string>
+ <string name="msg_kc_sub_algo_bad_encrpyt">Podklíč má příznak použitelnosti k šifrování, ale algoritmus není k šifrování vhodný.</string>
+ <string name="msg_kc_sub_algo_bad_sign">Podklíč má příznak použitelnosti k podepisování, ale algoritmus není k podepisování vhodný.</string>
+ <string name="msg_kc_success">Kanonizace klíčenky úspšná, žádné změny</string>
+ <plurals name="msg_kc_success_bad">
+ <item quantity="one">Kanonizace klíčenky úspšná, odebrán jeden chybný certifikát</item>
+ <item quantity="few">Kanonizace klíčenky úspšná, odebráno %d chybné certifikáty</item>
+ <item quantity="other">Kanonizace klíčenky úspšná, odebráno %d chybných certifikátů</item>
+ </plurals>
+ <string name="msg_kc_success_bad_and_red">Kanonizace klíčenky úspšná, odebráno %1$s chybných a %2$s nadbytečných certifikátů</string>
+ <plurals name="msg_kc_success_redundant">
+ <item quantity="one">Kanonizace klíčenky úspšná, odebrán jeden nadbytečný certifikát</item>
+ <item quantity="few">Kanonizace klíčenky úspšná, odebrány %d nadbytečné certifikáty</item>
+ <item quantity="other">Kanonizace klíčenky úspšná, odebráno %d nadbytečných certifikátů</item>
+ </plurals>
+ <string name="msg_kc_uid_bad_err">Odebírám chybný vlastní certifikát pro uživatele \'%s\'</string>
+ <string name="msg_kc_uid_bad_local">Odebírám uživatelské ID certifikátu s příznakem \'lokální\'</string>
+ <string name="msg_kc_uid_bad_time">Odebírám uživatelské ID s časovou značkou v budoucnu</string>
+ <string name="msg_kc_uid_bad_type">Odebírám uživatelské ID certifikátu neznámého typu (%s)</string>
+ <string name="msg_kc_uid_bad">Odebírám chybný vlastní certifikát pro uživatele \'%s\'</string>
+ <string name="msg_kc_uid_cert_dup">Odebrírám zastaralý vlastní certifikát pro uživatelské ID \'%s\'</string>
+ <string name="msg_kc_uid_foreign">Odebírám cizí uživatelské ID certifikátu od \'%s\'</string>
+ <string name="msg_kc_uid_revoke_dup">Odebírám nadbytečný zneplatňovací certifikát pro uživatelské ID \'%s\'</string>
+ <string name="msg_kc_uid_revoke_old">Odebírám zastaralý zneplatňovací certifikát pro uživatelské ID \'%s\'</string>
+ <string name="msg_kc_uid_no_cert">Nenalezen žádný platný vlastní certifikát pro uživatelské ID \'%s\', odebírám z klíčenky</string>
+ <string name="msg_kc_uid_remove">Odebírám neplatnou uživatelskou identitu \'%s\'</string>
+ <string name="msg_kc_uid_warn_encoding">Uživatelské ID neověřeno jako UTF-8!</string>
+ <string name="msg_kc_uat_warn_encoding">Uživatelské ID neověřeno jako UTF-8!</string>
<!--Keyring merging log entries-->
<!--createSecretKeyRing-->
<string name="msg_cr">Generuji nový hlavní klíč</string>
<string name="msg_cr_error_no_keysize">Nebyla specifikována eliptická křivka! Toto je chyba v programování a je třeba ji nahlásit!</string>
<!--modifySecretKeyRing-->
<string name="msg_mr">Upravuji klíčenku %s</string>
+ <string name="msg_mf_error_sub_stripped">Modifikace holého podklíče %s se nezdařila!</string>
<string name="msg_mf_uid_error_empty">Uživatelské ID nesmí být prázdné!</string>
<!--Consolidate-->
<string name="msg_con_error_db">Chyba otevírání databáze!</string>
<!--Edit Key (higher level than modify)-->
<!--Promote key-->
<!--Other messages used in OperationLogs-->
+ <string name="msg_ek_error_dummy">Nemohu editovat klíčenku s holým hlavním klíčem!</string>
<string name="msg_ek_error_not_found">Klíč nenalezen!</string>
<!--Messages for DecryptVerify operation-->
<!--Messages for VerifySignedLiteralData operation-->
@@ -597,9 +887,13 @@
<!--Linked Identity verification-->
<string name="msg_acc_saved">Účet uložen</string>
<!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_error_key_mismatch">Otisk klíče nesouhlasí s tím v důkazu</string>
+ <string name="msg_keybase_error_dns_fail">Stažení záznamu DNS TXT selhalo</string>
+ <string name="msg_keybase_error_msg_payload_mismatch">Rošifrovaný záznam důkazu nesouhlasí s očekávanou hodnotou</string>
<!--Messages for Mime parsing operation-->
<!--PassphraseCache-->
<!--Keyserver sync-->
+ <string name="keyserver_sync_orbot_notif_start">Spustit Orbot</string>
<!--First Time-->
<string name="first_time_text1">Převezměte opět kontrolu nad svým soukromím s OpenKeychain!</string>
<string name="first_time_skip">Přeskočit nastavení</string>
@@ -611,6 +905,7 @@
<string name="label_cert_type">Typ</string>
<string name="error_key_not_found">Klíč nenalezen!</string>
<string name="error_key_processing">Chyba zpracování klíče!</string>
+ <string name="key_stripped">holý</string>
<string name="key_unavailable">nedostupný</string>
<string name="secret_cannot_multiple">Vaše vlastní klíče lze mazat pouze jednotlivě!</string>
<string name="title_view_cert">Zobrazit detaily certifikátu</string>
@@ -619,10 +914,26 @@
<string name="error_no_encrypt_subkey">Není dostupný šifrovací podklíč!</string>
<string name="contact_show_key">Zobrazit klíč (%s)</string>
<string name="swipe_to_update">Potáhnout dolů pro aktualizaci z keyserveru</string>
+ <string name="user_id_none"><![CDATA[<žádný>]]></string>
<!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <string name="enter_passphrase">Zadejte heslo</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
<!--Other Linked Identity strings-->
+ <string name="section_linked_identities">Propojené identity</string>
+ <string name="share_log_dialog_share_button">Sdílet</string>
+ <string name="share_log_dialog_cancel_button">Zrušit</string>
+ <string name="menu_uids_save">Uložit</string>
+ <string name="remember">Zapamatovat</string>
+ <string name="security_token_error_pgp_app_not_installed">Žádná PGP aplikace nenalezena na bezpečnostním tokenu</string>
+ <string name="prompt_fidesmo_pgp_install_title">Instalovat PGP?</string>
+ <string name="prompt_fidesmo_pgp_install_message">Na vašem Fidesmo zařízení nebyla nalezena žádná PGP aplikace.</string>
+ <string name="prompt_fidesmo_pgp_install_button_positive">Instalovat</string>
+ <string name="prompt_fidesmo_pgp_install_button_negative">Zrušit</string>
+ <string name="prompt_fidesmo_app_install_title">Nainstalovat Fidesmo?</string>
+ <string name="prompt_fidesmo_app_install_message">K instalaci PGP potřebujete Fidesmo Android aplikaci.</string>
+ <string name="prompt_fidesmo_app_install_button_positive">Instalovat</string>
+ <string name="prompt_fidesmo_app_install_button_negative">Zrušit</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml
index 6eada434b..4ed5cf746 100644
--- a/OpenKeychain/src/main/res/values-de/strings.xml
+++ b/OpenKeychain/src/main/res/values-de/strings.xml
@@ -8,12 +8,13 @@
<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">Merkdauer von Passwörtern</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>
@@ -88,8 +90,8 @@
<string name="btn_add_files">Datei(en) hinzufügen</string>
<string name="btn_share_decrypted_text">Teilen</string>
<string name="btn_open_with">Öffnen mit…</string>
- <string name="btn_copy_decrypted_text">Entschlüsselten Text kopieren</string>
- <string name="btn_decrypt_clipboard">Aus Zwischenablage lesen</string>
+ <string name="btn_copy_decrypted_text">In die Zwischenablage kopieren</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">Merkdauer von Passwörtern</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>
@@ -451,6 +462,7 @@
<string name="help_tab_changelog">Changelog</string>
<string name="help_tab_about">Über</string>
<string name="help_about_version">Version:</string>
+ <string name="help_tab_donations">Spende</string>
<!--Import-->
<string name="import_tab_keyserver">Schlüsselserver</string>
<string name="import_tab_cloud">Schlüsselsuche</string>
@@ -671,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>
@@ -708,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>
@@ -740,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 !-->
@@ -750,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>
@@ -829,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">
@@ -846,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>
@@ -855,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-->
@@ -877,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>
@@ -918,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>
@@ -934,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>
@@ -962,49 +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_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>
@@ -1013,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>
@@ -1051,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>
@@ -1074,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>
@@ -1084,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>
@@ -1118,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>
@@ -1131,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>
@@ -1146,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">
@@ -1174,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>
@@ -1183,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>
@@ -1195,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>
@@ -1217,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>
@@ -1225,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>
@@ -1270,46 +1282,46 @@
<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">Verarbeite MIME Daten Struktur</string>
- <string name="msg_data_mime_ok">Parsen beendet</string>
+ <string name="msg_data_mime_charset">Zeichensatz ist \'%s\'</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">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">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_download_no_pgp_parts">NOCH ZU MACHEN: Plurale!</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">ein Teil der geladenen Datei ist ein gültiges OpenPGP-Objekt, aber kein OpenPGP-Schlüssel</item>
- <item quantity="other">Teile der geladenen Dateien sind gültige OpenPGP-Objekte, aber keine OpenPGP-Schlüssel</item>
- </plurals>
- <string name="msg_get_query_too_short">Die Suchanfrage ist zu kurz, bitte die Suchanfrage verfeinern!</string>
- <string name="msg_get_too_many_responses">Schlüsselsuchanfrage lieferte zu viele Kandidaten, bitte die Suchanfrage verfeinern!</string>
- <string name="msg_get_query_too_short_or_too_many_responses">Entweder keine oder zu viele Schlüssel wurden gefunden, bitte die Suchanfrage prä­zi­sie­ren!</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>
<string name="msg_download_query_failed">Beim Suchen der Schlüssel ist ein Fehler aufgetreten.</string>
<!--Messages for Keybase Verification operation-->
<string name="msg_keybase_verification">Versuche Keybase.io-Verifikation für %s</string>
@@ -1320,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>
@@ -1334,17 +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_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>
@@ -1359,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>
@@ -1391,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>
@@ -1422,63 +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>
@@ -1491,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>
@@ -1515,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>
@@ -1542,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_error_timeout">Zeitüberschreitung bei Verbindungsaufbau</string>
- <string name="linked_error_network">Netzwerk Fehler!</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>
@@ -1565,27 +1579,49 @@
<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 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>
<string name="cache_ttl_three_hours">für drei Stunden</string>
<string name="cache_ttl_one_day">für einen Tag</string>
<string name="cache_ttl_three_days">für drei Tage</string>
- <string name="settings_cache_select_three">Wähle bis zu drei</string>
- <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="cache_ttl_forever">für immer</string>
+ <string name="settings_cache_select_three">Wähle bis zu drei.</string>
+ <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">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>
+ <string name="prompt_fidesmo_pgp_install_button_negative">Abbrechen</string>
+ <string name="prompt_fidesmo_app_install_title">Fidesmo installieren?</string>
+ <string name="prompt_fidesmo_app_install_message">Fidesmo Android App wird benötigt um PGP zu installieren.</string>
+ <string name="prompt_fidesmo_app_install_button_positive">Installieren</string>
+ <string name="prompt_fidesmo_app_install_button_negative">Abbrechen</string>
+ <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>
+ <item>50 EUR</item>
+ <item>100 EUR</item>
+ </string-array>
</resources>
diff --git a/OpenKeychain/src/main/res/values-es-rMX/strings.xml b/OpenKeychain/src/main/res/values-es-rMX/strings.xml
index fb73ac050..4110883e1 100644
--- a/OpenKeychain/src/main/res/values-es-rMX/strings.xml
+++ b/OpenKeychain/src/main/res/values-es-rMX/strings.xml
@@ -81,7 +81,6 @@
<string name="btn_add_files">Agregar archivo(s)</string>
<string name="btn_share_decrypted_text">Compartir</string>
<string name="btn_open_with">Abrir con...</string>
- <string name="btn_copy_decrypted_text">Copiar texto descifrado</string>
<string name="btn_decrypt_clipboard">Leer desde el portapapeles</string>
<string name="btn_decrypt_files">Elegir archivo de entrada</string>
<string name="btn_encrypt_files">Cifrar archivos</string>
@@ -204,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 b5dd92164..dbea7dd03 100644
--- a/OpenKeychain/src/main/res/values-es/strings.xml
+++ b/OpenKeychain/src/main/res/values-es/strings.xml
@@ -13,7 +13,6 @@
<string name="title_preferences">Configuración</string>
<string name="title_api_registered_apps">Aplicaciones</string>
<string name="title_key_server_preference">Servidores de claves OpenPGP</string>
- <string name="title_cache_ttl_preference">Personalizar opciones de \"Recordar\"</string>
<string name="title_change_passphrase">Cambiar contraseña</string>
<string name="title_share_fingerprint_with">Compartir huella digital con...</string>
<string name="title_share_key">Compartir clave con...</string>
@@ -86,7 +85,6 @@
<string name="btn_add_files">Añadir fichero(s)</string>
<string name="btn_share_decrypted_text">Compartir</string>
<string name="btn_open_with">Abrir con...</string>
- <string name="btn_copy_decrypted_text">Copiar texto descifrado</string>
<string name="btn_decrypt_clipboard">Leer del portapapeles</string>
<string name="btn_decrypt_files">Seleccionar fichero de entrada</string>
<string name="btn_encrypt_files">Cifrar ficheros</string>
@@ -137,7 +135,6 @@
<string name="label_encryption_algorithm">Algoritmo de cifrado</string>
<string name="label_hash_algorithm">Algoritmo hash</string>
<string name="label_symmetric">Cifrar con contraseña</string>
- <string name="label_passphrase_cache_ttl">Personalizar opciones de \"Recordar\"</string>
<string name="label_passphrase_cache_subs">Recordar contraseñas por subclave</string>
<string name="label_message_compression">Compresión del texto</string>
<string name="label_file_compression">Compresión del fichero</string>
@@ -237,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>
@@ -639,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-->
@@ -1227,16 +1213,6 @@
<string name="msg_data_mime_type">Content-Type: %s</string>
<string name="msg_data_ok">Los datos se procesaron exitosamente</string>
<string name="msg_acc_saved">Cuenta guardada</string>
- <string name="msg_get_success">¡Descargado con éxito!</string>
- <string name="msg_get_no_valid_keys">¡No se encontraron claves válidas en el fichero/portapapeles!</string>
- <string name="msg_download_no_pgp_parts">PENDIENTE: ¡plurales!</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">parte del archivo cargado es un objeto OpenPGP válido pero no una clave OpenPGP</item>
- <item quantity="other">partes del archivo cargado son objetos OpenPGP válidos pero no claves OpenPGP</item>
- </plurals>
- <string name="msg_get_query_too_short">Solicitud de búsqueda demasiado corta. ¡Por favor refine su petición!</string>
- <string name="msg_get_too_many_responses">La solicitud de búsqueda de clave devolvió demasiados candidatos. ¡Por favor refine su petición!</string>
- <string name="msg_get_query_too_short_or_too_many_responses">O bien no hay claves o se han encontrado demasiadas. ¡Por favor mejore su petición!</string>
<string name="msg_download_query_failed">Ocurrió un error al buscar claves.</string>
<!--Messages for Keybase Verification operation-->
<string name="msg_keybase_verification">Intentando la verificación con Keybase para %s</string>
diff --git a/OpenKeychain/src/main/res/values-eu/strings.xml b/OpenKeychain/src/main/res/values-eu/strings.xml
index ef19d48d8..9965df050 100644
--- a/OpenKeychain/src/main/res/values-eu/strings.xml
+++ b/OpenKeychain/src/main/res/values-eu/strings.xml
@@ -8,12 +8,13 @@
<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>
<string name="title_api_registered_apps">Aplikazioak</string>
<string name="title_key_server_preference">OpenPGP giltza-zerbitzariak</string>
- <string name="title_cache_ttl_preference">Norbereratu \'Gogoratu\' Hautapenak</string>
+ <string name="title_cache_ttl_preference">Norbereratu \'Gogoratu\' hautapenak</string>
<string name="title_change_passphrase">Aldatu Sarhitza</string>
<string name="title_share_fingerprint_with">Elkarbanatu hatz-aztarna honekin...</string>
<string name="title_share_key">Elkarbanatu giltza honekin...</string>
@@ -32,10 +33,12 @@
<string name="title_help">Laguntza</string>
<string name="title_log_display">Oharra</string>
<string name="title_exchange_keys">Trukatu Giltzak</string>
+ <string name="title_advanced_key_info">Aurreratua</string>
<string name="title_delete_secret_key">Ezabatu ZURE \'%s\' giltza?</string>
<string name="title_manage_my_keys">Kudeatu nire giltzak</string>
<!--section-->
<string name="section_user_ids">Nortasunak</string>
+ <string name="section_security_token">Segurtasun Lekukoa</string>
<string name="section_linked_system_contact">Loturatutako Sistema Harremana</string>
<string name="section_keybase_proofs">Keybase.io Probak</string>
<string name="section_should_you_trust">Fidatu behar zara giltza honetaz?</string>
@@ -86,7 +89,7 @@
<string name="btn_add_files">Gehitu agiria(k)</string>
<string name="btn_share_decrypted_text">Elkarbanatu</string>
<string name="btn_open_with">Ireki honekin...</string>
- <string name="btn_copy_decrypted_text">Kopiatu dekriptaturiko idazkia</string>
+ <string name="btn_copy_decrypted_text">Kopiatu gakora</string>
<string name="btn_decrypt_clipboard">Irakurri gakotik</string>
<string name="btn_decrypt_files">Hautatu sarrera agiria</string>
<string name="btn_encrypt_files">Enkriptatu agiriak</string>
@@ -96,6 +99,7 @@
<string name="btn_add_keyserver">Gehitu</string>
<string name="btn_save_default">Gorde berezkoa bezala</string>
<string name="btn_saved">Gordeta!</string>
+ <string name="btn_not_matching">Ez dator bat</string>
<!--Content Description-->
<string name="cd_encrypt_files">Enkriptatu agiriak</string>
<string name="cd_exchange_keys">Trukatu giltzak</string>
@@ -114,6 +118,7 @@
<string name="menu_select_all">Hautatu denak</string>
<string name="menu_export_all_keys">Esportatu giltza guztiak</string>
<string name="menu_update_all_keys">Eguneratu giltza guztiak</string>
+ <string name="menu_advanced">Aurreratua</string>
<string name="menu_certify_fingerprint">Baieztatu hatz-aztarnarekin</string>
<string name="menu_certify_fingerprint_phrases">Baieztatu esaldiekin</string>
<string name="menu_share_log">Elkarbanatu oharra</string>
@@ -135,6 +140,7 @@
<string name="label_file_ascii_armor">Gaitu ASCII Armor</string>
<string name="label_write_version_header">Jakinarazi besteei OpenKeychain erabiltzen ari zarela</string>
<string name="label_write_version_header_summary">\'OpenKeychain v2.7\' idazten du OpenPGP sinadura, idazkia zifratzen du eta giltzak esportatzen ditu</string>
+ <string name="label_use_num_keypad_for_security_token_pin">Erabili zenbaki teklatua Segurtasun Lekukoa PIN-erako</string>
<string name="label_asymmetric_from">Hasi saioa honekin:</string>
<string name="label_to">Enkriptatu hona</string>
<string name="label_delete_after_encryption">Ezabatu agiriak enkriptatu ondoren</string>
@@ -142,13 +148,14 @@
<string name="label_encryption_algorithm">Enkriptaketa algoritmoa</string>
<string name="label_hash_algorithm">Hash algoritmoa</string>
<string name="label_symmetric">Enkriptatu sarhitzarekin</string>
- <string name="label_passphrase_cache_ttl">Norbereratu \'Gogoratu\' Hautapenak</string>
+ <string name="label_passphrase_cache_ttl">Norbereratu \'Gogoratu\' hautapenak</string>
<string name="label_passphrase_cache_subs">Gogoratu sarhitzak azpigiltzaz</string>
<string name="label_message_compression">Idazki konpresioa</string>
<string name="label_file_compression">Agiri konpresioa</string>
<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>
@@ -182,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>
@@ -249,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>
@@ -273,6 +282,8 @@
<string name="passphrase_for_backup">Sartu babeskopia kodea</string>
<string name="passphrase_for">Sartu \'%s\'-rako sarhitza</string>
<string name="pin_for">Sartu PIN-a \'%s\'-rako</string>
+ <string name="security_token_pin_for">Sartu PIN-a Segurtasun Lekukoara sartzeko \'%s\'-rentzat</string>
+ <string name="security_token_nfc_finished">Utzi Segurtasun Lekukoa orain.</string>
<string name="file_delete_confirmation_title">Ezabatu jatorrizko agiriak?</string>
<string name="file_delete_confirmation">Hurrengo agiriak ezabatu egingo dira: %s</string>
<string name="file_delete_successful">%1$d -&gt; %2$d-tik agiri ezbatu dira.%3$s</string>
@@ -314,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>
@@ -440,6 +452,7 @@
<string name="help_tab_changelog">Aldaketa-oharra</string>
<string name="help_tab_about">Honi buruz</string>
<string name="help_about_version">Bertsioa:</string>
+ <string name="help_tab_donations">Dirulaguntza</string>
<!--Import-->
<string name="import_tab_keyserver">Giltza-zerbitzaria</string>
<string name="import_tab_cloud">Giltza Bilaketa</string>
@@ -527,6 +540,8 @@
<string name="intent_send_encrypt">Enkriptatu OpenKeychain-rekin</string>
<string name="intent_send_decrypt">Dekriptatu OpenKeychain-rekin</string>
<!--Remote API-->
+ <string name="api_settings_show_info">Erakutsi argibide aurreratuak</string>
+ <string name="api_settings_hide_info">Ezkutatu argibide aurreratuak</string>
<string name="api_settings_no_key">Ez da giltzarik hautatu</string>
<string name="api_settings_select_key">Hautatu giltza</string>
<string name="api_settings_create_key">Sortu giltza berria</string>
@@ -539,6 +554,7 @@
<string name="api_settings_package_name">Pakete Izena</string>
<string name="api_settings_package_certificate">Pakete Egiaztagiriaren SHA-256</string>
<string name="api_settings_accounts">Kontuak (API zaharra)</string>
+ <string name="api_settings_advanced">Aurreratua</string>
<string name="api_settings_allowed_keys">Ahalbidetutako Giltzak</string>
<string name="api_settings_settings">Ezarpenak</string>
<string name="api_settings_key">Kontuaren giltza:</string>
@@ -593,6 +609,7 @@
<string name="key_view_action_share_nfc">Elkarbanatu NFC gain</string>
<string name="key_view_action_upload">Igo giltza-zerbitzarira</string>
<string name="key_view_tab_main">Argibide Nagusiak</string>
+ <string name="key_view_tab_start">Hasi</string>
<string name="key_view_tab_share">Elkarbanatu</string>
<string name="key_view_tab_keys">Azpigiltzak</string>
<string name="key_view_tab_certs">Egiaztagiriak</string>
@@ -633,6 +650,7 @@
<string name="keybase_twitter_proof">Txio bat</string>
<string name="keybase_dns_proof">DNS TXT grabaketa bat</string>
<string name="keybase_web_site_proof">Idazki agiri bat</string>
+ <string name="keybase_github_proof"> </string>
<string name="keybase_reddit_proof">JSON agiri bat</string>
<string name="keybase_verify">Egiaztatu</string>
<!--Edit key-->
@@ -649,10 +667,18 @@
</string-array>
<string name="edit_key_edit_user_id_revoked">Nortasun hau ukatua izan da. Hau ezin da desegin.</string>
<string name="edit_key_edit_subkey_title">Hautatu ekintza bat!</string>
+ <string-array name="edit_key_edit_subkey">
+ <item>Aldatu Epemuga</item>
+ <item>Ukatu Azpigiltza</item>
+ <item>Strip Azpigiltza</item>
+ <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>
+ <string name="edit_key_error_bad_security_token_size">Giltza neurria ez dago Segurtasun Lekukoak sostengatua!</string>
<!--Create key-->
<string name="create_key_upload">Aldiberetu Internetekin</string>
<string name="create_key_empty">Eremu hau beharrezkoa da</string>
@@ -871,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>
@@ -894,6 +921,7 @@
<string name="msg_mf_error_passphrase_master">Akats larria giltza maisua dekriptatzerakoan! Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
<string name="msg_mf_error_pgp">Barneko OpenPGP akatsa!</string>
<string name="msg_mf_error_sig">Sinadura salbuespena!</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">Giltza ikur ezegokiak Segurtasun Lekukoa giltzarako.</string>
<string name="msg_mf_master">Maisu egiaztagiriak aldatzen</string>
<string name="msg_mf_notation_empty">Ohar pakete hutsa gehitzen</string>
<string name="msg_mf_notation_pin">PIN jakinarazpen paketea gehitzen</string>
@@ -912,6 +940,8 @@
<string name="msg_mf_subkey_new_id">Azpigiltza berriaren ID-a: %s</string>
<string name="msg_mf_error_past_expiry">Iraungitze eguna ezin da iraganean izan!</string>
<string name="msg_mf_subkey_revoke">%s azpigiltza ukatzen</string>
+ <string name="msg_mf_keytocard_start">%s azpigiltza Segurtasun Lekukora mugitzen</string>
+ <string name="msg_mf_keytocard_finish">Mugituta %1$s -&gt; %2$s Segurtasun Lekukora</string>
<string name="msg_mf_success">Giltza-uztaia ongi aldatu da</string>
<string name="msg_mf_uid_add">Erabiltzaile ID-a %s gehitzen</string>
<string name="msg_mf_uid_primary">Lehen erabiltzaile ID-a %s-ra aldatzen</string>
@@ -1086,6 +1116,7 @@
<string name="msg_import_fetch_error_keyserver">Ezin da giltza berreskuratu giltza-zerbitzarietatik: %s</string>
<string name="msg_import_fetch_error_keyserver_secret">Ezin da giltza inportatu giltza-zerbitzaritik!</string>
<string name="msg_import_fetch_keybase">keybase.io-tik berreskuratzen: %s</string>
+ <string name="msg_import_fetch_facebook">Facebook-etik berreskuratzen: %s</string>
<string name="msg_import_fetch_keyserver">Giltza-zerbitzaritik berreskuratzen: %s</string>
<string name="msg_import_fetch_keyserver_ok">Giltza ongi berreskuratu da</string>
<string name="msg_import_keyserver">%s giltza-zerbitzaria erabiltzen</string>
@@ -1159,7 +1190,9 @@
<string name="msg_lv_fetch_error_nothing">Baliabidea ez da aurkitu!</string>
<string name="msg_bench">Zenbait eragiketaren alderaketa-azterketa egiten...</string>
<string name="msg_bench_enc_time">Enkriptaketa denbora: %ss</string>
+ <string name="msg_bench_enc_time_avg">Bataz-besteko denbora 5M enkriptatzeko: %ss</string>
<string name="msg_bench_dec_time">Dekriptaketa denbora: %ss</string>
+ <string name="msg_bench_dec_time_avg">Bataz-besteko denbora 5M dekriptatzeko: %ss</string>
<string name="msg_bench_success">Alderaketa-azterketa osatuta!</string>
<string name="msg_data">Sarrera datuak prozesatzen</string>
<string name="msg_data_openpgp">OpenPGP datuak prozesatzen saitzen</string>
@@ -1172,6 +1205,8 @@
<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>
<string name="msg_data_mime_none">Ez da MIME egiturarik aurkitu</string>
@@ -1181,14 +1216,10 @@
<string name="msg_data_skip_mime">MIME azterketa jauzi egiten</string>
<string name="msg_acc_saved">Kontua gordeta</string>
<string name="msg_get_success">Ongi jeitsi da!</string>
+ <string name="msg_get_file_not_found">Sarrera agiria ez da aurkitu!</string>
<string name="msg_get_no_valid_keys">Ez da baliozko giltzarik aurkitu agiri/gakoan!</string>
- <string name="msg_download_no_pgp_parts">TODO: anitzak!</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">gertatutako agiriaren zati bat baliozko OpenPGP objetua da baina ez OpenPGP giltza</item>
- <item quantity="other">gertatutako agiriaren zati batzuk baliozko OpenPGP objetuak dira baina ez OpenPGP giltzak</item>
- </plurals>
- <string name="msg_get_query_too_short">Bilaketa eskaera laburregia. Mesedez zehaztu zure eskaera!</string>
<string name="msg_get_too_many_responses">Giltza bilaketa eskaerak hautagai gehiegi itzuli ditu. Mesedez zehaztu zure eskaera!</string>
+ <string name="msg_get_query_too_short">Bilaketa eskaera laburregia. Mesedez zehaztu zure eskaera!</string>
<string name="msg_get_query_too_short_or_too_many_responses">Ez dago giltzarik edo gehiegi aurkitu dira. Mesedez hobetu zure eskaera!</string>
<string name="msg_download_query_failed">Akats bat gertatu da giltzak bilatzerakoan.</string>
<!--Messages for Keybase Verification operation-->
@@ -1221,7 +1252,10 @@
<string name="first_time_text1">Berreskuratu zure pribatutasuna OpenKeychain-ekin!</string>
<string name="first_time_create_key">Sortu nire giltza</string>
<string name="first_time_import_key">Inportatu giltza agiritik</string>
+ <string name="first_time_security_token">Erabili Segurtasun Lekukoa</string>
+ <string name="first_time_security_token_subtitle">(Fidesmo, YubiKey NEO, SIGILANCE, …)</string>
<string name="first_time_skip">Jauzi Ezarpena</string>
+ <string name="first_time_blank_security_token_yes">Erabili Segurtasun Lekuko hau</string>
<string name="backup_text">Zure jabetzako giltzak dituzten babeskopiak inoiz ez dira besteekin elkarbanatu behar!</string>
<string name="backup_all">Giltza guztiak + zeure jabetzako giltzak</string>
<string name="backup_public_keys">Giltza guztiak</string>
@@ -1242,6 +1276,7 @@
<string name="label_cert_type">Mota</string>
<string name="error_key_not_found">Giltza ez da aurkitu!</string>
<string name="error_key_processing">Akatsa giltza prozesatzerakoan!</string>
+ <string name="key_divert">desbideratu Segurtasun Lekukoara</string>
<string name="key_no_passphrase">sarhitzik gabe</string>
<string name="key_unavailable">eskuraezina</string>
<string name="secret_cannot_multiple">Zure jabetzako giltzak banaka bakarrik ezabatu daitezke!</string>
@@ -1290,11 +1325,34 @@
<string name="nfc_wrong_tag">Etiketa Okerra. Mesedez saiatu berriro.</string>
<string name="enable_nfc">Mesedez gaitu NFC zure ezarpenetan</string>
<string name="no_nfc_support">Gailu honek ez du NFC sostengatzen</string>
+ <string name="nfc_write_successful">Ongi idatzi da NFC etiketan</string>
<string name="unlocked">Desblokeatuta</string>
<string name="nfc_settings">Ezarpenak</string>
+ <string name="snack_security_token_view">Ikusi</string>
+ <string name="snack_security_token_import">Inportatu</string>
<string name="button_bind_key">Blindatu Giltza</string>
+ <string name="security_token_serial_no">Serie Zbk: %s</string>
+ <string name="security_token_key_holder_not_set"><![CDATA[Key holder: <not set>]]></string>
<string name="btn_import">Inportatu</string>
<string name="btn_reset">Berrezarri</string>
+ <string name="security_token_import_radio">Inportatu giltza</string>
+ <string name="security_token_reset_radio">Berrezarri Segurtasun Lekukoa</string>
+ <string name="security_token_error">Akatsa: %s</string>
+ <plurals name="security_token_error_pin">
+ <item quantity="one">PIN okerra!\n%d saiakera gelditzen da.</item>
+ <item quantity="other">PIN okerra!\n%d saiakera gelditzen dira.</item>
+ </plurals>
+ <string name="security_token_error_terminated">Segurtasun Lekukoa amaiera egoeran.</string>
+ <string name="security_token_error_wrong_length">Sartutako PIN-a laburregia da. PIN-ek gutxienez 6 digitoko luzera dute.</string>
+ <string name="security_token_error_conditions_not_satisfied">Erabilpen baldintzak ez dira bete.</string>
+ <string name="security_token_error_security_not_satisfied">Segurtasun egoera ez da bete.</string>
+ <string name="security_token_error_authentication_blocked">PIN-a blokeatuta saiakera gehiegiren ondoren.</string>
+ <string name="security_token_error_data_not_found">Giltza edo objetua ez da aurkitu.</string>
+ <string name="security_token_error_unknown">Akats Ezezaguna</string>
+ <string name="security_token_error_bad_data">Segurtasun Lekukoak datu baliogabeak jakinarazi ditu.</string>
+ <string name="security_token_error_header">Segurtasun Lekukoak %s byte baliogabe jakinarazi ditu.</string>
+ <string name="security_token_error_iso_dep_not_supported">Etiketak ez du ISO-DEP (ISO 14443-4) sostengatzen</string>
+ <string name="security_token_error_try_again">Saitu berriro</string>
<string name="btn_delete_original">Ezabatu jatorrizko agiria</string>
<string name="snack_encrypt_filenames_on">Agirizenak enkriptatuta <b>daude</b>.</string>
<string name="snack_encrypt_filenames_off">Agirizenak <b>ez daude</b> enkriptatuta.</string>
@@ -1420,13 +1478,35 @@
<string name="title_edit_identities">Editatu Nortasunak</string>
<string name="title_edit_subkeys">Editatu Azpigiltzak</string>
<string name="btn_search_for_query">Bilatu\n\'%s\'</string>
- <string name="cache_ttl_lock_screen">Ikusleihoa Itzali arte</string>
- <string name="cache_ttl_one_hour">Ordubetez</string>
- <string name="cache_ttl_three_hours">Hiru Orduz</string>
- <string name="cache_ttl_one_day">Egun batez</string>
- <string name="cache_ttl_three_days">Hiru Egunez</string>
+ <string name="cache_ttl_lock_screen">ikusleihoa itzali arte</string>
+ <string name="cache_ttl_ten_minutes">hamar minutuz</string>
+ <string name="cache_ttl_thirty_minutes">hogeita-hamar minutuz</string>
+ <string name="cache_ttl_one_hour">ordubetez</string>
+ <string name="cache_ttl_three_hours">hiru orduz</string>
+ <string name="cache_ttl_one_day">egun batez</string>
+ <string name="cache_ttl_three_days">hiru egunez</string>
+ <string name="cache_ttl_forever">betirako</string>
<string name="settings_cache_select_three">Hautatu hiru.</string>
<string name="settings_cache_ttl_at_least_one">Gai bat hautatu behar da gutxienez!</string>
<string name="settings_cache_ttl_max_three">Ezin dira hiru gai baino gehiago hautatu!</string>
<string name="remember">Gogoratu</string>
+ <string name="security_token_error_pgp_app_not_installed">Ez da PGP aplikaziorik aurkitu segurtasun lekukoan</string>
+ <string name="prompt_fidesmo_pgp_install_title">Ezarri PGP?</string>
+ <string name="prompt_fidesmo_pgp_install_message">Ez dago PGP aplikazio eskuragarririk zure Fidesmo gailuan.</string>
+ <string name="prompt_fidesmo_pgp_install_button_positive">Ezarri</string>
+ <string name="prompt_fidesmo_pgp_install_button_negative">Ezeztatu</string>
+ <string name="prompt_fidesmo_app_install_title">Ezarri Fidesmo?</string>
+ <string name="prompt_fidesmo_app_install_message">PGP ezartzeko Fidesmo Android aplikazioa behar duzu.</string>
+ <string name="prompt_fidesmo_app_install_button_positive">Ezarri</string>
+ <string name="prompt_fidesmo_app_install_button_negative">Ezeztatu</string>
+ <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>
+ <item>50 EUR</item>
+ <item>100 EUR</item>
+ </string-array>
</resources>
diff --git a/OpenKeychain/src/main/res/values-fa/strings.xml b/OpenKeychain/src/main/res/values-fa/strings.xml
index 4794b676a..388e08057 100644
--- a/OpenKeychain/src/main/res/values-fa/strings.xml
+++ b/OpenKeychain/src/main/res/values-fa/strings.xml
@@ -62,7 +62,6 @@
<string name="btn_match">اثر انگشت‌ها مطابقت دارد</string>
<string name="btn_create_key">ساخت کلید</string>
<string name="btn_add_files">اضافه کردن فایل(ها)</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,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>
@@ -253,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 0a989fcca..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>
@@ -88,7 +90,7 @@
<string name="btn_add_files">Ajouter un/des fichier(s)</string>
<string name="btn_share_decrypted_text">Partager</string>
<string name="btn_open_with">Ouvrir avec...</string>
- <string name="btn_copy_decrypted_text">Copier le texte déchiffré</string>
+ <string name="btn_copy_decrypted_text">Copier vers le presse-papiers</string>
<string name="btn_decrypt_clipboard">Lire du presse-papiers</string>
<string name="btn_decrypt_files">Choisir le fichier d\'entrée</string>
<string name="btn_encrypt_files">Chiffrer des fichiers</string>
@@ -147,13 +149,14 @@
<string name="label_encryption_algorithm">Algorithme de chiffrement</string>
<string name="label_hash_algorithm">Algorithme de hachage</string>
<string name="label_symmetric">Chiffrer avec un mot de passe</string>
- <string name="label_passphrase_cache_ttl">Personnaliser les choix « Se souvenir »</string>
+ <string name="label_passphrase_cache_ttl">Personnaliser les choix « Mémorisation »</string>
<string name="label_passphrase_cache_subs">Mémoriser les mots de passe par sous-clefs</string>
<string name="label_message_compression">Compression de texte</string>
<string name="label_file_compression">Compression des fichiers</string>
<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>
@@ -451,6 +462,7 @@
<string name="help_tab_changelog">Journal des changements</string>
<string name="help_tab_about">À propos de l\'appli</string>
<string name="help_about_version">Version :</string>
+ <string name="help_tab_donations">Faire un don</string>
<!--Import-->
<string name="import_tab_keyserver">Serveur de clefs</string>
<string name="import_tab_cloud">Recherche de clefs</string>
@@ -674,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>
@@ -1292,6 +1304,10 @@
<string name="msg_data_mime_filename">Nom de fichier : « %s »</string>
<string name="msg_data_mime_from_extension">Le type MIME est deviné à partir de l\'extension</string>
<string name="msg_data_mime_length">Longueur du contenu : %s</string>
+ <string name="msg_data_mime_charset">Le jeu de caractères est « %s »</string>
+ <string name="msg_data_mime_charset_faulty">Le jeu de caractères est « %s » mais le décodage a échoué !</string>
+ <string name="msg_data_mime_charset_guess">Le jeu de caractères semble être « %s »</string>
+ <string name="msg_data_mime_charset_unknown">Le jeu de caractères est inconnu ou les données ne sont pas du texte.</string>
<string name="msg_data_mime">Analyse de la structure des données MIME</string>
<string name="msg_data_mime_ok">Fin de l\'analyse</string>
<string name="msg_data_mime_none">Aucune structure MIME trouvée</string>
@@ -1301,15 +1317,11 @@
<string name="msg_data_skip_mime">L\'analyse MIME est ignorée</string>
<string name="msg_acc_saved">Compte enregistré</string>
<string name="msg_get_success">Téléchargement réussi !</string>
- <string name="msg_get_no_valid_keys">Aucune clef valide n\'a été trouvée dans le fichier/presse-papiers |</string>
- <string name="msg_download_no_pgp_parts">À FAIRE : les pluriels !</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">une partie du fichier chargé est un objet OpenPGP valide mais pas une clef OpenPGP</item>
- <item quantity="other">certaines parties du fichier chargé sont des objets OpenPGP valides mais pas des clefs OpenPGP</item>
- </plurals>
- <string name="msg_get_query_too_short">La requête de recherche est trop courte. Veuillez raffiner votre requête !</string>
- <string name="msg_get_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez raffiner votre requête !</string>
- <string name="msg_get_query_too_short_or_too_many_responses">Soit aucune clef ou trop de clefs ont été trouvées. Veuillez améliorer votre requête !</string>
+ <string name="msg_get_file_not_found">Fichier d\'entrée introuvable !</string>
+ <string name="msg_get_no_valid_keys">Aucune clef valide n\'a été trouvée dans le fichier/presse-papiers !</string>
+ <string name="msg_get_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez la préciser !</string>
+ <string name="msg_get_query_too_short">La requête de recherche est trop courte. Veuillez la préciser !</string>
+ <string name="msg_get_query_too_short_or_too_many_responses">Soit aucune clef n\'a été trouvée, ou trop de clefs. Veuillez améliorer votre requête !</string>
<string name="msg_download_query_failed">Une erreur est survenue lors de la recherche de clefs.</string>
<!--Messages for Keybase Verification operation-->
<string name="msg_keybase_verification">Tentative de vérification « keybase » pour %s</string>
@@ -1341,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>
@@ -1384,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-->
@@ -1423,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>
@@ -1581,7 +1593,7 @@
<string name="title_edit_identities">Modifier les identités</string>
<string name="title_edit_subkeys">Modifier les sous-clefs</string>
<string name="btn_search_for_query">Rechercher\n« %s »</string>
- <string name="cache_ttl_lock_screen">jusqu\'à ce que l\'écran soit éteint</string>
+ <string name="cache_ttl_lock_screen">jusqu\'à l\'extinction de l\'écran</string>
<string name="cache_ttl_ten_minutes">pendant dix minutes</string>
<string name="cache_ttl_thirty_minutes">pendant trente minutes</string>
<string name="cache_ttl_one_hour">pendant une heure</string>
@@ -1593,4 +1605,23 @@
<string name="settings_cache_ttl_at_least_one">Vous devez choisir au moins un élément !</string>
<string name="settings_cache_ttl_max_three">Vous ne pouvez pas choisir plus de trois éléments !</string>
<string name="remember">Mémorisation</string>
+ <string name="security_token_error_pgp_app_not_installed">Aucune appli PGP n\'a été trouvée sur le jeton de sécurité</string>
+ <string name="prompt_fidesmo_pgp_install_title">Installer PGP ?</string>
+ <string name="prompt_fidesmo_pgp_install_message">Il n\'y avait aucune appli PGP disponible sur votre appareil Fidesmo.</string>
+ <string name="prompt_fidesmo_pgp_install_button_positive">Installer</string>
+ <string name="prompt_fidesmo_pgp_install_button_negative">Annuler</string>
+ <string name="prompt_fidesmo_app_install_title">Installer Fidesmo ?</string>
+ <string name="prompt_fidesmo_app_install_message">Pour installer PGP, il vous faut l\'appli Fidesmo pour Android.</string>
+ <string name="prompt_fidesmo_app_install_button_positive">Installer</string>
+ <string name="prompt_fidesmo_app_install_button_negative">Annuler</string>
+ <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>
+ <item>50 EUR</item>
+ <item>100 EUR</item>
+ </string-array>
</resources>
diff --git a/OpenKeychain/src/main/res/values-hi/strings.xml b/OpenKeychain/src/main/res/values-hi/strings.xml
index b0e2fc1b8..82c22b939 100644
--- a/OpenKeychain/src/main/res/values-hi/strings.xml
+++ b/OpenKeychain/src/main/res/values-hi/strings.xml
@@ -2,11 +2,23 @@
<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">ओपनकीचैन </string>
<!--title-->
+ <string name="title_encrypt_text">एन्क्रिप्ट</string>
+ <string name="title_encrypt_files">एन्क्रिप्ट</string>
+ <string name="title_decrypt">डिक्रिप्ट </string>
+ <string name="title_add_subkey">उपकुंजी जोड़ें</string>
+ <string name="title_edit_key">संपादित कुंजी</string>
+ <string name="title_linked_create">एक लिंक्डन पहचान बनाने</string>
+ <string name="title_preferences">सेटिंग</string>
+ <string name="title_api_registered_apps">एप्लिकेशन</string>
+ <string name="title_change_passphrase">पासवर्ड परिवर्तन </string>
<!--section-->
+ <string name="section_encrypt">एन्क्रिप्ट</string>
<!--button-->
<!--Content Description-->
<!--menu-->
+ <string name="menu_preferences">सेटिंग</string>
<!--label-->
<!--label shown in Android settings under the OpenKeychain account-->
<!--Proxy Preferences-->
@@ -15,7 +27,6 @@
<!--InstallDialogFragment strings-->
<!--StartOrbotDialogFragment strings-->
<!--choice-->
- <!--key flags-->
<!--sentences-->
<!--errors
no punctuation, all lowercase,
@@ -42,6 +53,7 @@
<!--Certify result toast-->
<!--Intent labels-->
<!--Remote API-->
+ <string name="api_settings_settings">सेटिंग</string>
<!--Share-->
<!--retry upload dialog-->
<!--Delete or revoke private key dialog-->
@@ -51,10 +63,13 @@
<!--Key trust-->
<!--keybase proof stuff-->
<!--Edit key-->
+ <string name="edit_key_action_change_passphrase">
+पासवर्ड परिवर्तन </string>
<!--Create key-->
<!--View key-->
<!--Add/Edit keyserver-->
<!--Navigation Drawer-->
+ <string name="nav_apps">एप्लिकेशन</string>
<!--hints-->
<!--certs-->
<!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
@@ -84,5 +99,6 @@
<!--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>-->
+ <string name="nfc_settings">सेटिंग</string>
<!--Other Linked Identity strings-->
</resources>
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 17f2d2798..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,10 +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_copy_decrypted_text">Copia testo decifrato</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>
@@ -82,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>
@@ -95,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>
@@ -103,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>
@@ -122,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>
@@ -140,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>
@@ -186,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>
@@ -235,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"-->
@@ -271,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-->
@@ -281,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>
@@ -326,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>
@@ -355,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>
@@ -407,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>
@@ -424,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-->
@@ -433,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>
@@ -441,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>
@@ -464,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>
@@ -629,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>
@@ -672,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-->
@@ -688,23 +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>
- <string name="msg_download_no_pgp_parts">DA FARE: plurali!</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">parte del file caricato e\' un oggetto OpenPGP valido, ma non una chave OpenPGP</item>
- <item quantity="other">parti del file caricato sono oggetti OpenPGP validi, ma non chavi OpenPGP</item>
- </plurals>
<!--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>
@@ -712,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>
@@ -727,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 a986025ce..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>
@@ -88,7 +90,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_copy_decrypted_text">クリップボードへコピー</string>
<string name="btn_decrypt_clipboard">クリップボードから読み取り</string>
<string name="btn_decrypt_files">入力ファイルの選択</string>
<string name="btn_encrypt_files">ファイルの暗号化</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>
@@ -451,6 +462,7 @@
<string name="help_tab_changelog">Changelog</string>
<string name="help_tab_about">これについて</string>
<string name="help_about_version">バージョン:</string>
+ <string name="help_tab_donations">寄付</string>
<!--Import-->
<string name="import_tab_keyserver">鍵サーバ</string>
<string name="import_tab_cloud">鍵の検索</string>
@@ -665,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>
@@ -1269,6 +1281,10 @@
<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>
@@ -1278,14 +1294,11 @@
<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_download_no_pgp_parts">TODO: 複数部分!</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="other">読み込んだファイルのOpenPGPオブジェクト部分は正しいですが、OpenPGPの鍵ではありません</item>
- </plurals>
- <string name="msg_get_query_too_short">鍵検索のクエリが短かすぎます。クエリを精密化してください!</string>
- <string name="msg_get_too_many_responses">鍵検索のクエリが沢山の候補を返しました。クエリを精密化してください!</string>
- <string name="msg_get_query_too_short_or_too_many_responses">鍵がまったく無いか、多すぎる鍵が見付かりました。クエリを改善してください!</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 のkeybase検証を試行中</string>
@@ -1398,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>
@@ -1566,4 +1579,23 @@
<string name="settings_cache_ttl_at_least_one">少なくとも項目を1つ選択する必要があります!</string>
<string name="settings_cache_ttl_max_three">3つ以上の項目を選択することはできません!</string>
<string name="remember">記憶する</string>
+ <string name="security_token_error_pgp_app_not_installed">セキュリティトークンでPGPアプリが見つかりません</string>
+ <string name="prompt_fidesmo_pgp_install_title">PGPをインストールしますか?</string>
+ <string name="prompt_fidesmo_pgp_install_message">お使いのFidesmoデバイスには利用できるPGPアプリがありません。</string>
+ <string name="prompt_fidesmo_pgp_install_button_positive">インストール</string>
+ <string name="prompt_fidesmo_pgp_install_button_negative">キャンセル</string>
+ <string name="prompt_fidesmo_app_install_title">Fidesmoをインストールしますか?</string>
+ <string name="prompt_fidesmo_app_install_message">PGPをインストールするにはFidesmo Androidアプリが必要です。</string>
+ <string name="prompt_fidesmo_app_install_button_positive">インストール</string>
+ <string name="prompt_fidesmo_app_install_button_negative">キャンセル</string>
+ <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>
+ <item>50 ユーロ</item>
+ <item>100 ユーロ</item>
+ </string-array>
</resources>
diff --git a/OpenKeychain/src/main/res/values-kn/strings.xml b/OpenKeychain/src/main/res/values-kn/strings.xml
new file mode 100644
index 000000000..86053fba1
--- /dev/null
+++ b/OpenKeychain/src/main/res/values-kn/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-ko/strings.xml b/OpenKeychain/src/main/res/values-ko/strings.xml
index e20f309ce..7a920db40 100644
--- a/OpenKeychain/src/main/res/values-ko/strings.xml
+++ b/OpenKeychain/src/main/res/values-ko/strings.xml
@@ -84,7 +84,6 @@
<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>
@@ -226,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
new file mode 100644
index 000000000..b90ac76ad
--- /dev/null
+++ b/OpenKeychain/src/main/res/values-nb/strings.xml
@@ -0,0 +1,203 @@
+<?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").-->
+ <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-->
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
+ <string name="secret_key">Hemmelig nøkkel:</string>
+ <!--choice-->
+ <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"-->
+ <!--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-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml
index 41a207016..723c312a5 100644
--- a/OpenKeychain/src/main/res/values-nl/strings.xml
+++ b/OpenKeychain/src/main/res/values-nl/strings.xml
@@ -64,7 +64,6 @@
<string name="btn_view_cert_key">Toon certificatiesleutel</string>
<string name="btn_create_key">Sleutel aanmaken</string>
<string name="btn_add_files">Bestand(en) toevoegen</string>
- <string name="btn_copy_decrypted_text">Ontsleutelde tekst kopiëren</string>
<string name="btn_decrypt_clipboard">Lezen van klembord</string>
<string name="btn_decrypt_files">Kies invoerbestand</string>
<string name="btn_encrypt_files">Bestanden versleutelen</string>
@@ -186,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>
@@ -529,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-->
@@ -1040,16 +1028,6 @@
</plurals>
<!--Linked Identity verification-->
<string name="msg_acc_saved">Account opgeslaan</string>
- <string name="msg_get_success">Succesvol gedownload!</string>
- <string name="msg_get_no_valid_keys">Geen geldige sleutels gevonden in bestand/klembord!</string>
- <string name="msg_download_no_pgp_parts">TODO: meervouden!</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">Deel van het geladen bestand is geldig OpenPGP object maar niet een OpenPGP sleutel</item>
- <item quantity="other">Delen van het geladen bestand zijn geldige OpenPGP objecten maar niet OpenPGP sleutels</item>
- </plurals>
- <string name="msg_get_query_too_short">Zoekopdracht te kort. Gelieve je zoekopdracht te verfijnen!</string>
- <string name="msg_get_too_many_responses">Zoekopdracht gaf te veel kandidaten. Gelieve je zoekopdracht te verfijnen!</string>
- <string name="msg_get_query_too_short_or_too_many_responses">Geen of te veel sleutels werden gevonden. Gelieve je zoekopdracht te verfijnen!</string>
<string name="msg_download_query_failed">Er trad een fout op bij het zoeken naar sleutels.</string>
<!--Messages for Keybase Verification operation-->
<string name="msg_keybase_verification">Keybaseverificatie voor %s wordt geprobeerd</string>
diff --git a/OpenKeychain/src/main/res/values-pl/strings.xml b/OpenKeychain/src/main/res/values-pl/strings.xml
index 85e9f83bb..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-->
@@ -517,12 +506,6 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
</plurals>
<!--Linked Identity verification-->
<string name="msg_acc_saved">Zapisano konto</string>
- <string name="msg_get_success">Pobrano pomyślnie!</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">Część wczytanego pliku jest poprawnym obiektem OpenPGP, ale nie jest kluczem OpenPGP</item>
- <item quantity="few">Część wczytanego pliku to poprawne obiekty OpenPGP, ale nie są kluczami OpenPGP</item>
- <item quantity="other">Część wczytanego pliku to poprawne obiekty OpenPGP, ale nie są kluczami OpenPGP</item>
- </plurals>
<!--Messages for Keybase Verification operation-->
<!--Messages for Mime parsing operation-->
<!--PassphraseCache-->
diff --git a/OpenKeychain/src/main/res/values-pt-rBR/strings.xml b/OpenKeychain/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000..472e4cacf
--- /dev/null
+++ b/OpenKeychain/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,493 @@
+<?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").-->
+ <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-->
+ <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 229bbeb39..7f276ef51 100644
--- a/OpenKeychain/src/main/res/values-ru/strings.xml
+++ b/OpenKeychain/src/main/res/values-ru/strings.xml
@@ -8,11 +8,13 @@
<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>
<string name="title_api_registered_apps">Приложения</string>
<string name="title_key_server_preference">Серверы OpenPGP</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>
@@ -31,10 +33,13 @@
<string name="title_help">Помощь</string>
<string name="title_log_display">Журнал</string>
<string name="title_exchange_keys">Обмен ключами</string>
+ <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>
<string name="section_linked_system_contact">Связанные контакты</string>
<string name="section_keybase_proofs">Доказательства Keybase.io</string>
<string name="section_should_you_trust">Должны ли Вы доверять этому ключу?</string>
@@ -43,11 +48,12 @@
<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>
<string name="section_sync_settings">Синхронизация</string>
- <string name="section_sync_settings_summary">Автоматическое обновление ключей, </string>
+ <string name="section_sync_settings_summary">Автоматическое обновление ключей, связывание контактов</string>
<string name="section_experimental_features">Экспериментальные возможности</string>
<string name="section_certify">Подтвердить</string>
<string name="section_actions">Действия</string>
@@ -78,12 +84,13 @@
<string name="btn_match_phrases">Совпадающие фразы</string>
<string name="btn_share_encrypted_signed">Зашифровать/подписать и поделиться текстом</string>
<string name="btn_copy_encrypted_signed">Зашифровать/подписать и скопировать текст</string>
+ <string name="btn_paste_encrypted_signed">Зашифровать/подписать и вставить текст</string>
<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_copy_decrypted_text">Копировать в буфер</string>
<string name="btn_decrypt_clipboard">Прочитать из буфера</string>
<string name="btn_decrypt_files">Выберите входной файл</string>
<string name="btn_encrypt_files">Зашифровать файлы</string>
@@ -93,7 +100,12 @@
<string name="btn_add_keyserver">Добавить</string>
<string name="btn_save_default">Сохранить по умолчанию</string>
<string name="btn_saved">Сохранено!</string>
+ <string name="btn_not_matching">Не совпадают</string>
<!--Content Description-->
+ <string name="cd_encrypt_files">Шифровать файлы</string>
+ <string name="cd_exchange_keys">Обмен ключами</string>
+ <string name="cd_encrypt_text">Шифровать текст</string>
+ <string name="cd_share_nfc">Отправить через NFC</string>
<!--menu-->
<string name="menu_preferences">Настройки</string>
<string name="menu_help">Помощь</string>
@@ -107,6 +119,11 @@
<string name="menu_select_all">Выбрать все</string>
<string name="menu_export_all_keys">Экспорт всех ключей</string>
<string name="menu_update_all_keys">Обновить все ключи</string>
+ <string name="menu_advanced">Дополнительно</string>
+ <string name="menu_certify_fingerprint">Подтверждение отпечатком</string>
+ <string name="menu_certify_fingerprint_phrases">Подтверждение кодовой фразой</string>
+ <string name="menu_share_log">Отправить журнал</string>
+ <string name="menu_change_password">Изменить пароль</string>
<string name="menu_keyserver_add">Добавить</string>
<!--label-->
<string name="label_message">Текст</string>
@@ -115,6 +132,7 @@
<string name="label_file_colon">Файл:</string>
<string name="label_no_passphrase">Без пароля</string>
<string name="label_passphrase">Пароль</string>
+ <string name="label_pin">PIN-код</string>
<string name="label_unlock">Идет разблокировка...</string>
<string name="label_passphrase_again">Повторите пароль</string>
<string name="label_show_passphrase">Показать пароль</string>
@@ -123,18 +141,22 @@
<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>
<string name="label_delete_after_decryption">Удалить после шифрования</string>
<string name="label_encryption_algorithm">Алгоритм шифрования</string>
<string name="label_hash_algorithm">ХЭШ-алгоритм</string>
<string name="label_symmetric">Зашифровать с паролем</string>
- <string name="label_passphrase_cache_subs">Запомнить пароли для подключей</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>
<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>
@@ -144,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>
@@ -156,23 +178,33 @@
<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>
<!--label shown in Android settings under the OpenKeychain account-->
+ <string name="keyserver_sync_settings_title">Автообновление ключей</string>
<string name="label_experimental_settings_desc_title">Предупреждение</string>
<string name="label_experimental_settings_desc_summary">Эти новые возможности ещё не закончены и/или пока только изучаются. Проще говоря, не стоит полагаться на их безопасность. Пожалуйста, не сообщайте о связанных с ними проблемах!</string>
+ <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-->
<string name="pref_proxy_tor_title">Использовать Tor</string>
<string name="pref_proxy_tor_summary">Требуется установка Orbot</string>
@@ -195,10 +227,13 @@
<string name="orbot_install_dialog_ignore_tor">Не использовать Tor</string>
<!--StartOrbotDialogFragment strings-->
<string name="orbot_start_dialog_title">Запустить Orbot?</string>
+ <string name="orbot_start_dialog_content">Похоже, что Orbot не запущен. Хотите запустить и соединиться с Tor?</string>
<string name="orbot_start_btn">Запустить Orbot</string>
<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>
@@ -227,32 +262,44 @@
<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_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>
<string name="passphrases_do_not_match">Пароли не совпадают.</string>
<string name="passphrase_must_not_be_empty">Пожалуйста, введите пароль.</string>
<string name="passphrase_for_symmetric_encryption">Введите пароль</string>
+ <string name="passphrase_for_backup">Введите код восстановления</string>
<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>
@@ -260,27 +307,41 @@
<string name="error_no_encryption_or_signature_key">Выберите хотя бы один ключ для шифрования или подписи.</string>
<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>
+ <string name="secret_key_deletion_confirmation">После удаления вы не сможете расшифровать сообщения/файлы, зашифрованные этим ключом и потеряете все сделанные им подтверждения ключей!</string>
<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>
<string name="fingerprint_copied_to_clipboard">Отпечаток ключа скопирован в буфер обмена!</string>
<string name="select_key_to_certify">Выберите ключ, используемый для подтверждения!</string>
<string name="text_copied_to_clipboard">Тест скопирован в буфер обмена!</string>
+ <string name="how_to_import">Как это импортировать на моём ПК?</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">не удалены. Удалите их самостоятельно!</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>
@@ -301,9 +362,11 @@
<string name="error_nothing_import_selected">Ключи для импорта не выбраны!</string>
<string name="error_contacts_key_id_missing">Ошибка извлечения идентификатора ключа из контактов!</string>
<string name="error_generic_report_bug">Выявлена ошибка. Пожалуйста, сообщите о ней разработчику.</string>
+ <string name="error_denied_storage_permission">Невозможно прочитать файлы с накопителя, потому что доступ запрещён!</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Не подписано</string>
<string name="decrypt_result_invalid_signature">Неверная подпись!</string>
+ <string name="decrypt_result_insecure_cryptography">Неправильная подпись (небезопасная криптография)!</string>
<string name="decrypt_result_signature_uncertified">Подписано <b>неподтверждённым</b> ключом!</string>
<string name="decrypt_result_signature_secret">Подписано Вашим ключом</string>
<string name="decrypt_result_signature_certified">Подписано подтверждённым ключом</string>
@@ -312,8 +375,10 @@
<string name="decrypt_result_signature_missing_key">Подписано <b>неизвестным ключом</b>!</string>
<string name="decrypt_result_encrypted">Зашифровано</string>
<string name="decrypt_result_not_encrypted">Не зашифровано</string>
+ <string name="decrypt_result_insecure">Небезопасное шифрование</string>
<string name="decrypt_result_action_show">Показать</string>
<string name="decrypt_result_action_Lookup">Искать</string>
+ <string name="decrypt_invalid_text">Или подпись недействительна, или ключ был аннулирован. Вы не можете быть уверены в том, кто написал текст. Вы всё ещё хотите посмотреть его?</string>
<string name="decrypt_invalid_button">Мне понятны все риски, показать!</string>
<!--Add keys-->
<string name="add_keys_my_key">Мой ключ:</string>
@@ -323,6 +388,8 @@
<string name="progress_cancelling">отмена...</string>
<string name="progress_saving">сохранение...</string>
<string name="progress_importing">импорт...</string>
+ <string name="progress_benchmarking">проверка производительности…</string>
+ <string name="progress_revoking_uploading">Аннулирование и отправка ключа ...</string>
<string name="progress_updating">Обновление ключей...</string>
<string name="progress_exporting">экспорт...</string>
<string name="progress_uploading">загружается...</string>
@@ -344,6 +411,8 @@
<string name="progress_modify_subkeystrip">отделение доп.ключей...</string>
<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>
@@ -370,6 +439,8 @@
<string name="progress_deleting">удаление ключей...</string>
<string name="progress_con_saving">объединение: сохранение в кэш...</string>
<string name="progress_con_reimport">объединение: реимпорт...</string>
+ <string name="progress_verifying_keyserver_connection">проверка соединения…</string>
+ <string name="progress_starting_orbot">Запуск Orbot…</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Искать через Имя, Email...</string>
<!--key bit length selections-->
@@ -400,6 +471,7 @@
<string name="help_tab_changelog">Изменения</string>
<string name="help_tab_about">О программе</string>
<string name="help_about_version">Версия:</string>
+ <string name="help_tab_donations">Пожертвование</string>
<!--Import-->
<string name="import_tab_keyserver">Сервер ключей</string>
<string name="import_tab_cloud">Поиск ключа</string>
@@ -412,24 +484,97 @@
<string name="import_qr_code_button">Сканировать QR код...</string>
<string name="import_qr_code_text">Расположите вашу камеру над QR кодом!</string>
<!--Import from URL-->
+ <string name="import_url_warn_no_search_parameter">Поисковый запрос не определён. Вы можете вручную искать на этом сервере ключей.</string>
<!--Generic result toast-->
<string name="snackbar_details">Сведения</string>
<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)-->
+ <string name="revoke_ok">Ключ успешно аннулирован.</string>
+ <string name="revoke_fail">Ошибка при аннулировании ключа!</string>
+ <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>
<string name="intent_send_encrypt">OpenKeychain: Зашифровать</string>
<string name="intent_send_decrypt">OpenKeychain: Расшифровать</string>
<!--Remote API-->
+ <string name="api_settings_show_info">Показать подробную информацию</string>
+ <string name="api_settings_hide_info">Скрыть подробную информацию</string>
<string name="api_settings_no_key">Ключ не выбран</string>
<string name="api_settings_select_key">Выбрать ключ</string>
<string name="api_settings_create_key">Создать новый ключ</string>
@@ -440,7 +585,9 @@
<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>
<string name="api_settings_settings">Настройки</string>
<string name="api_settings_key">Ключ аккаунта:</string>
@@ -451,6 +598,8 @@
<string name="api_register_allow">Разрешить доступ</string>
<string name="api_register_disallow">Запретить доступ</string>
<string name="api_register_error_select_key">Пожалуйста, выберите ключ!</string>
+ <string name="api_select_pub_keys_missing_text">Не найдены ключи для этих адресов электронной почты:</string>
+ <string name="api_select_pub_keys_dublicates_text">Больше чем один ключ существует для этих адресов электронной почты:</string>
<string name="api_select_pub_keys_text">Пожалуйста, проверьте получателей!</string>
<string name="api_select_pub_keys_text_no_user_ids">Пожалуйста, выберите получателей!</string>
<string name="api_error_wrong_signature">Проверка подписи пакета не удалась! Если вы установили программу из другого источника, отзовите для неё доступ к этой программе или обновите право доступа.</string>
@@ -460,8 +609,18 @@
<string name="share_qr_code_dialog_title">Отправить как QR код</string>
<string name="share_nfc_dialog">Отправить через NFC</string>
<!--retry upload dialog-->
+ <string name="retry_up_dialog_title">Загрузка не удалась</string>
+ <string name="retry_up_dialog_message">Загрузка не удалась. Хотите повторить операцию?</string>
+ <string name="retry_up_dialog_btn_reupload">Повторить операцию</string>
+ <string name="retry_up_dialog_btn_cancel">Отменить операцию</string>
<!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_message">Если вы больше не хотите использовать этот ключ, его следует аннулировать и загрузить на сервер. Выберите «Только удалить», если вы хотите удалить ключ из OpenKeychain, но продолжать использовать его от где-нибудь ещё.</string>
+ <string name="del_rev_dialog_title">Аннулирование/Удаление ключа \'%s\'</string>
+ <string name="del_rev_dialog_btn_revoke">Аннулировать и отправить</string>
+ <string name="del_rev_dialog_btn_delete">Удалить только</string>
<!--Delete Or Revoke Dialog spinner-->
+ <string name="del_rev_dialog_choice_delete">Удалить только</string>
+ <string name="del_rev_dialog_choice_rev_upload">Аннулировать и отправить</string>
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 ключ выбран.</item>
@@ -471,8 +630,12 @@
</plurals>
<string name="key_list_empty_text1">Ключи не найдены!</string>
<string name="key_list_filter_show_all">Показать все ключи</string>
+ <string name="key_list_filter_show_certified">Показывать только подтверждённые ключи</string>
+ <string name="key_list_fab_qr_code">Сканировать QR-код</string>
<string name="key_list_fab_search">Поиск ключа</string>
+ <string name="key_list_fab_import">Импорт из файла</string>
<!--Key view-->
+ <string name="key_view_action_edit">Изменить</string>
<string name="key_view_action_encrypt">Зашифровать текст</string>
<string name="key_view_action_encrypt_files">файлы</string>
<string name="key_view_action_certify">Подтвердить ключ</string>
@@ -481,17 +644,32 @@
<string name="key_view_action_share_nfc">Отправить через NFC</string>
<string name="key_view_action_upload">Загрузить на сервер ключей</string>
<string name="key_view_tab_main">Основные данные</string>
+ <string name="key_view_tab_start">Начать</string>
<string name="key_view_tab_share">Отправить...</string>
<string name="key_view_tab_keys">Доп. ключи</string>
<string name="key_view_tab_certs">Сертификация</string>
<string name="key_view_tab_keybase">Keybase.io</string>
<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_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_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_github">Известен на GitHub как %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_unknown">Неизвестный тип доказательства %s</string>
<string name="keybase_proof_failure">К сожалению это доказательство не может быть верифицировано.</string>
<string name="keybase_unknown_proof_failure">Неопознанная проблема с проверкой доказательства </string>
<string name="keybase_problem_fetching_evidence">Проблема с доказательством</string>
@@ -501,6 +679,9 @@
<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>
<string name="keybase_web_site_proof">Текстовый файл</string>
<string name="keybase_reddit_proof">JSON file</string>
@@ -519,12 +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>
@@ -532,13 +721,22 @@
<string name="create_key_rsa">(3 доп. ключа, RSA, 4096 bit)</string>
<string name="create_key_custom">(произвольная конфигурация ключа)</string>
<string name="create_key_name_text">Выберите имя для данного ключа. Это может быть полное имя, например, \'Иван Петров\', или сокращенно, как например, \'Ванька\'.</string>
- <string name="create_key_email_text">Введите Ваш главный адрес эл.почты, используемый для безопасной переписки.</string>
- <string name="create_key_passphrase_text">Выберите надежный пароль. Он защитит Ваш ключ, в случае кражи вашего устройства.</string>
+ <string name="create_key_email_text">Введите ваш главный адрес эл. почты, используемый для безопасной переписки.</string>
+ <string name="create_key_passphrase_text">Выберите надежный пароль. Он защитит ваш ключ, в случае кражи вашего устройства.</string>
<string name="create_key_hint_full_name">Полное имя или псевдоним</string>
<string name="create_key_edit">Изменить конфигурацию ключа</string>
- <string name="create_key_add_email">Введите адрес эл.почты</string>
+ <string name="create_key_add_email">Введите адрес эл. почты</string>
+ <string name="create_key_add_email_text">Дополнительные адреса электронной почты также связаны с этим ключом и могут быть использованы для безопасной связи.</string>
<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>
+ <string name="create_key_yubi_key_pin_insecure">Пожалуйста, введите сложный PIN-код, а не 000000, 123456 или подобные комбинации.</string>
<!--View key-->
<string name="view_key_revoked">Аннулирован: Ключ не должен использоваться когда-либо в будущем!</string>
<string name="view_key_expired">Срок годности истёк: Контакт должен продлить срок!</string>
@@ -549,8 +747,14 @@
<string name="view_key_fragment_no_system_contact">&lt;нет&gt;</string>
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Добавить сервер ключей</string>
+ <string name="edit_keyserver_dialog_title">Изменить сервер ключей</string>
+ <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-->
<string name="nav_keys">Ключи</string>
<string name="nav_encrypt_decrypt">Зашифровать/Расшифровать</string>
@@ -558,6 +762,7 @@
<string name="drawer_open">Открыть панель навигации</string>
<string name="drawer_close">Закрыть панель навигации</string>
<string name="my_keys">Мои ключи</string>
+ <string name="nav_backup">Резервирование/Восстановление</string>
<!--hints-->
<string name="encrypt_content_edit_text_hint">Напишите текст</string>
<!--certs-->
@@ -583,6 +788,8 @@
<string name="msg_ip_error_op_exc">Действие прервано из-за ошибки базы</string>
<string name="msg_ip_error_remote_ex">Действие прервано из-за внутренней ошибки</string>
<string name="msg_ip">Импорт связки публичных ключей %s</string>
+ <string name="msg_ip_fingerprint_error">Отпечаток импортируемого ключа не соответствует ожидаемому!</string>
+ <string name="msg_ip_fingerprint_ok">Проверка отпечатка пройдена</string>
<string name="msg_ip_insert_keyring">Кодирование связки ключей</string>
<string name="msg_ip_insert_keys">Разбор ключей</string>
<string name="msg_ip_prepare">Подготовка операций</string>
@@ -607,6 +814,7 @@
<string name="msg_ip_master_flags_xxxa">Основные флаги: удостоверить</string>
<string name="msg_ip_master_flags_xxxx">Основные флаги: нет</string>
<string name="msg_ip_merge_public">Объединение импортированных данных с существующей связкой публичных ключей</string>
+ <string name="msg_ip_merge_secret">Объединение импортированных данных с существующей связкой секретных ключей</string>
<string name="msg_ip_subkey">Обработка доп. ключа %s</string>
<string name="msg_ip_subkey_expired">Срок годности доп. ключа истек %s</string>
<string name="msg_ip_subkey_expires">Срок годности доп. ключа истекает %s</string>
@@ -637,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>
@@ -650,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>
@@ -660,10 +886,12 @@
<string name="msg_is_importing_subkeys">Обработка секретных доп. ключей</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_merge_special">Объединение само-сертифицированных данных из публичных ключей</string>
<string name="msg_is_pubring_generate">Формирование публичной связки из секретной связки</string>
<string name="msg_is_subkey_nonexistent">Доп. ключ %s недоступен в секретном ключе</string>
<string name="msg_is_subkey_ok">Секретный доп. ключ %s отмечен как доступный</string>
+ <string name="msg_is_subkey_empty">Секретный доп. ключ %s отмечен как доступный, с пустым паролем</string>
<string name="msg_is_subkey_pin">Секретный доп. ключ %s отмечен как доступный, с PIN</string>
<string name="msg_is_subkey_stripped">Секретный доп. ключ %s отмечен как отделённый</string>
<string name="msg_is_success_identical">Связка ключей не содержит новых данных, нечего делать</string>
@@ -677,18 +905,22 @@
<string name="msg_kc_error_dup_key">Доп. ключ %s найден дважды в связке. Связка некорректна, не импортируется!</string>
<string name="msg_kc_master">Подготовка основного ключа</string>
<string name="msg_kc_master_bad_type">Удаление сертификата основного ключа неизвестного типа (%s)</string>
+ <string name="msg_kc_master_bad_local">Удаление сертификата основного ключа с \"локальным\" флагом</string>
<string name="msg_kc_master_bad_err">Удаление плохого сертификата основного ключа</string>
<string name="msg_kc_master_bad_time">Удаление сертификата аннулирования ключа с отметкой времени, находящейся в будущем</string>
<string name="msg_kc_master_bad_type_uid">Удаление сертификата ID в некорректном положении</string>
<string name="msg_kc_master_bad">Удаление плохого сертификата основного ключа</string>
+ <string name="msg_kc_master_local">Удаление сертификата основного ключа с \"локальным\" флагом</string>
<string name="msg_kc_revoke_dup">Удаление лишнего сертификата аннулирования ключа</string>
<string name="msg_kc_notation_dup">Удаление лишнего сертификата обозначения</string>
<string name="msg_kc_notation_empty">Удаление пустого сертификата обозначения</string>
<string name="msg_kc_sub">Обработка доп. ключа %s</string>
<string name="msg_kc_sub_bad">Удаление неверного сертификата связывания доп. ключа</string>
<string name="msg_kc_sub_bad_err">Удаление плохого сертификата связывания доп. ключа</string>
+ <string name="msg_kc_sub_bad_local">Удаление сертификата связывания доп. ключа \"локальным\" флагом</string>
<string name="msg_kc_sub_bad_keyid">Несоответствие id издателя связывания доп. ключа</string>
<string name="msg_kc_sub_bad_time">Удаление сертификата связывания доп. ключа с отметкой времени, находящейся в будущем</string>
+ <string name="msg_kc_sub_bad_time_early">Сертификата связывания доп. ключа создан ранее, чем его ключ!</string>
<string name="msg_kc_sub_bad_type">Неизвестный тип сертификата доп. ключа: %s</string>
<string name="msg_kc_sub_dup">Удаление лишнего сертификата связывания доп. ключа</string>
<string name="msg_kc_sub_primary_bad">Удаление сертификата связывания доп. ключа вследствие неверного основного сертификата связывания</string>
@@ -702,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>
@@ -715,6 +959,7 @@
<string name="msg_kc_uid_no_cert">Нет допустимого самостоятельного сертификата для ID \'%s\', удаление из связки</string>
<string name="msg_kc_uid_remove">Удаление неверного ID \'%s\'</string>
<string name="msg_kc_uid_dup">Удаление повторяющегося ID \'%s\'. Связка содержит их два. Это может привести к отсутствию сертификатов!</string>
+ <string name="msg_kc_uid_too_many">Удаление ID пользователя \'%s\'. Более 100 идентификаторов пользователей не импортируются!</string>
<string name="msg_kc_uid_warn_encoding">ID пользователя не соответствует UTF-8!</string>
<string name="msg_kc_uat_jpeg">Обработка атрибута пользователя типа JPEG</string>
<string name="msg_kc_uat_unknown">Обработка атрибута пользователя неизвестного типа</string>
@@ -746,6 +991,7 @@
<string name="msg_cr_error_no_user_id">Связки должны создаваться с хотя бы одним ID пользователя!</string>
<string name="msg_cr_error_no_certify">Основной ключ должен иметь флаг сертификата!</string>
<string name="msg_cr_error_null_expiry">Срок годности не может быть \'такой же как раньше\' при создании ключа. Это программная ошибка, пожалуйста, сообщите об этом!</string>
+ <string name="msg_cr_error_keysize_2048">Размер ключа должен быть больше или равен 2048!</string>
<string name="msg_cr_error_no_curve">Не задан размер ключа! Это программная ошибка, пожалуйста, сообщите об этом!</string>
<string name="msg_cr_error_no_keysize">Не задана эллиптическая кривая! Это программная ошибка, пожалуйста, сообщите об этом!</string>
<string name="msg_cr_error_internal_pgp">Внутренняя ошибка OpenPGP!</string>
@@ -756,70 +1002,171 @@
<string name="msg_cr_error_flags_ecdh">Выбраны плохие флаги ключа, ECDH не может быть использован для подписи!</string>
<!--modifySecretKeyRing-->
<string name="msg_mr">Изменение связки %s</string>
+ <string name="msg_mf_divert">Будет отвлекать на токен безопасности для криптографических операций</string>
<string name="msg_mf_error_encode">Ошибка кодирования!</string>
<string name="msg_mf_error_fingerprint">Отпечаток ключа не совпадает с представленным!</string>
<string name="msg_mf_error_keyid">Нет ID ключа. Это программная ошибка. Пожалуйста, сообщите об этом!</string>
<string name="msg_mf_error_integrity">Внутренняя ошибка, сбой проверки целостности!</string>
<string name="msg_mf_error_master_none">Не найден основной сертификат! (Все отозваны?)</string>
+ <string name="msg_mf_error_noexist_primary">Указан некорректный ID основного пользователя!</string>
+ <string name="msg_mf_error_noexist_revoke">Для аннулирования определён некорректный ID пользователя!</string>
+ <string name="msg_mf_error_restricted">Попытка выполнить ограниченную операцию без пароля! Это ошибка программирования, пожалуйста, сообщите об ошибке!</string>
<string name="msg_mf_error_revoked_primary">Аннулированные идентификаторы не могут быть основными!</string>
+ <string name="msg_mf_error_null_expiry">При создании доп. ключа время окончания срока годности не может быть \'такое же как раньше\'. Это ошибка программирования, пожалуйста, сообщите об ошибке!</string>
<string name="msg_mf_error_noop">Выполнять нечего!</string>
+ <string name="msg_mf_error_passphrase_master">Неустранимая ошибка расшифровки главного ключа! Это, вероятно, ошибка программирования, отправьте сообщение об ошибке!</string>
<string name="msg_mf_error_pgp">Внутренняя ошибка OpenPGP!</string>
<string name="msg_mf_error_sig">Ошибка подписи!</string>
+ <string name="msg_mf_error_sub_stripped">Невозможно изменить отделённый доп. ключ %s!</string>
<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>
<string name="msg_mf_error_past_expiry">Срок годности не может быть в прошлом!</string>
<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>
<!--Consolidate-->
<string name="msg_con">Консолидация базы данных</string>
<string name="msg_con_error_bad_state">Консолидация началась но база не кэширована! Это программная ошибка, пожалуйста, сообщите об этом.</string>
+ <string name="msg_con_error_concurrent">Консолидация прервана, уже запущена на другом потоке!</string>
<string name="msg_con_save_secret">Сохранение секретной связки ключей</string>
<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>
<string name="msg_con_error_io_public">Ошибка записи публичных ключей в кэш!</string>
<string name="msg_con_error_io_secret">Ошибка записи приватных ключей в кэш!</string>
<string name="msg_con_error_public">Ошибка переимпортирования публичных ключей!</string>
<string name="msg_con_error_secret">Ошибка переимпортирования приватных ключей!</string>
+ <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_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>
+ <string name="msg_dc_clear_meta_size">Размер файла: %s</string>
<string name="msg_dc_clear_meta_size_unknown">Неизвестный размер файла</string>
+ <string name="msg_dc_clear_meta_time">Время изменения: %s</string>
<string name="msg_dc_clear_signature_bad">Проверка подписи НЕ ПРОЙДЕНА!</string>
<string name="msg_dc_clear_signature_check">Проверка подписи данных</string>
<string name="msg_dc_clear_signature_ok">Проверка подписи ПРОЙДЕНА</string>
<string name="msg_dc_clear_signature">Сохранение данных подписи</string>
<string name="msg_dc_error_bad_passphrase">Ошибка разблокировки ключа, неправильный пароль!</string>
+ <string name="msg_dc_error_sym_passphrase">Ошибка расшифровки данных! (Неправильная ключевая фраза?)</string>
+ <string name="msg_dc_error_corrupt_data">Данные повреждены!</string>
<string name="msg_dc_error_extract_key">Неизвестная ошибка разблокировения ключа!</string>
+ <string name="msg_dc_error_integrity_check">Ошибка проверки целостности!</string>
+ <string name="msg_dc_error_invalid_data">Зашифрованные или подписанные данные OpenPGP не найдены!</string>
+ <string name="msg_dc_error_io">Ошибка при открытии входного потока данных!</string>
+ <string name="msg_dc_error_input">Ошибка при открытии входного потока данных!</string>
<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>
<string name="msg_dc_pending_passphrase">Требуется пароль, жду действий пользователя...</string>
+ <string name="msg_dc_prep_streams">Подготовка потоков для расшифровки</string>
<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>
+ <string name="msg_vl_clear_meta_size">Размер: %s</string>
<string name="msg_vl_clear_signature_check">Проверка подписи данных</string>
+ <string name="msg_vl_error_integrity_check">Ошибка проверки целостности!</string>
<string name="msg_vl_ok">ОК</string>
<!--Messages for SignEncrypt operation-->
<string name="msg_se">Начало операции подписи/шифровки</string>
@@ -839,66 +1186,255 @@
<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>
<string name="msg_crt_unlock">Разблокировка основного ключа</string>
+ <string name="msg_crt_success">Подписанные идентификаторы</string>
<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>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">часть загруженного файла содержит данные OpenPGP, но это не ключ</item>
- <item quantity="few">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
- <item quantity="many">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
- <item quantity="other">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
- </plurals>
+ <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>
@@ -910,22 +1446,204 @@
<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">Владелец ключа: %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>
+ <string name="security_token_error_authentication_blocked">PIN-код заблокирован после слишком многих попыток.</string>
+ <string name="security_token_error_data_not_found">Ключ или объект не найден.</string>
+ <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>
+ <string name="snack_encrypt_filenames_off">Имена файлов <b>не шифруются</b>.</string>
+ <string name="snack_armor_on">Результат кодируется как текст.</string>
+ <string name="snack_armor_off">Результат кодируется как двоичный.</string>
+ <string name="snack_compression_on">Сжатие <b>включено</b>.</string>
+ <string name="snack_compression_off">Сжатие <b>отключено</b>.</string>
+ <string name="error_loading_keys">Ошибка загрузки ключей!</string>
+ <string name="error_empty_log">(ошибка, пустой журнал)</string>
+ <string name="error_reading_text">Не удалось прочитать ввод для расшифровки!</string>
+ <string name="error_reading_aosp">Ошибка чтения данных, это ошибка в клиенте электронной почты Android! (Статья #290)</string>
+ <string name="error_reading_k9">Получены неполные данные, попробуйте включить \'Загружать полное сообщение\' в K-9 Mail!</string>
+ <string name="filename_unknown">Неизвестное имя файла (нажмите для открытия)</string>
+ <string name="filename_unknown_text">Текст (нажмите для просмотра)</string>
+ <string name="filename_keys">Резервный ключ (нажмите для импортирования)</string>
+ <string name="intent_show">Показать Подписанное/Зашифрованное содержимое</string>
+ <string name="intent_share">Отправить Подписанное/Зашифрованное содержимое</string>
+ <string name="view_internal">Смотреть в OpenKeychain</string>
+ <string name="error_preparing_data">Ошибка подготовки данных!</string>
+ <string name="label_clip_title">Зашифрованные данные</string>
+ <string name="progress_processing">Обработка...</string>
+ <string name="error_saving_file">Ошибка при сохранении файла!</string>
+ <string name="file_saved">Файл сохранён!</string>
+ <string name="file_delete_ok">Оригинальный файл удалён.</string>
+ <string name="file_delete_none">Файл не удаляется! (Уже удалён?)</string>
+ <string name="file_delete_exception">Оригинальный файл не может быть удалён!</string>
+ <string name="error_clipboard_empty">Буфер обмена пуст!</string>
+ <string name="error_clipboard_copy">Ошибка копирования данных в буфер обмена!</string>
+ <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>
+ <string name="linked_verify_pending">Ещё не проверено</string>
+ <string name="linked_need_verify">Ресурс должен быть проверен, прежде чем продолжать!</string>
+ <string name="menu_linked_add_identity">Связать с аккаунтом</string>
<string name="section_linked_identities">Связанные идентификаторы</string>
<string name="btn_finish">Завершить</string>
- <string name="linked_title_twitter">Твиттер</string>
+ <string name="linked_title_https">Сайт (HTTPS)</string>
+ <string name="linked_title_dns">Доменное имя (DNS)</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>
<string name="linked_button_confirm">Подтвердить</string>
+ <string name="linked_button_view">Просмотр</string>
+ <string name="linked_text_verifying">Проверка…</string>
<string name="linked_text_error">Ошибка</string>
- <string name="share_log_dialog_share_button">Опубликовать</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>
+ <string name="linked_error_auth_failed">Авторизация не удалась!</string>
+ <string name="linked_error_timeout">Время соединения вышло!</string>
+ <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>
+ <string name="backup_code_ok">Код принят!</string>
+ <string name="btn_code_wrotedown">ОК, я записал его!</string>
+ <string name="backup_code_wrong">Резервный код введён неверно!\nВы записали его правильно?</string>
+ <string name="btn_backup_share">Отправить резервную копию</string>
+ <string name="btn_backup_save">Сохранить резервную копию</string>
+ <string name="snack_backup_error_saving">Ошибка при сохранении резервной копии!</string>
+ <string name="snack_backup_saved">Резервная копия сохранена</string>
+ <string name="snack_backup_exists">Резервная копия уже существует!</string>
+ <string name="snack_backup_saved_dir">Сохранено в папке OpenKeychain</string>
+ <string name="btn_backup_back">Вернуться для проверки</string>
+ <string name="snack_text_too_long">Текст слишком длинный, чтобы показать его полностью!</string>
+ <string name="snack_shared_text_too_long">Общий текст был сокращён, потому что он был слишком большим!</string>
+ <string name="share_log_dialog_title">Отправить журнал?</string>
+ <string name="share_log_dialog_message">Хотя журналы могут быть супер полезными для разработчиков, чтобы найти ошибки в OpenKeychain, они могут потенциально содержать конфиденциальную информацию, такую как данные об обновлённых ключах. Пожалуйста, убедитесь, что у вас всё в порядке с распространением этой информации.</string>
+ <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>
+ <string name="btn_search_for_query">Искать\n\'%s\'</string>
+ <string name="cache_ttl_lock_screen">до отключения экрана</string>
+ <string name="cache_ttl_ten_minutes">в течение десяти минут</string>
+ <string name="cache_ttl_thirty_minutes">в течение тридцати минут</string>
+ <string name="cache_ttl_one_hour">в течение часа</string>
+ <string name="cache_ttl_three_hours">в течение трёх часов</string>
+ <string name="cache_ttl_one_day">в течение дня</string>
+ <string name="cache_ttl_three_days">в течение трёх дней</string>
+ <string name="cache_ttl_forever">всегда</string>
+ <string name="settings_cache_select_three">Выберите до трёх.</string>
+ <string name="settings_cache_ttl_at_least_one">По крайней мере один элемент должен быть выбран!</string>
+ <string name="settings_cache_ttl_max_three">Нельзя выбрать более трёх элементов!</string>
+ <string name="remember">Помнить</string>
+ <string name="security_token_error_pgp_app_not_installed">Нет приложения PGP на токене безопасности</string>
+ <string name="prompt_fidesmo_pgp_install_title">Установить PGP?</string>
+ <string name="prompt_fidesmo_pgp_install_message">На вашем устройстве Fidesmo нет приложения PGP.</string>
+ <string name="prompt_fidesmo_pgp_install_button_positive">Установить</string>
+ <string name="prompt_fidesmo_pgp_install_button_negative">Отмена</string>
+ <string name="prompt_fidesmo_app_install_title">Установить Fidesmo?</string>
+ <string name="prompt_fidesmo_app_install_message">Для установки PGP вам необходимо приложение Fidesmo.</string>
+ <string name="prompt_fidesmo_app_install_button_positive">Установить</string>
+ <string name="prompt_fidesmo_app_install_button_negative">Отмена</string>
+ <string name="help_donation_paypal_item">Пожертвование на OpenKeychain</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml
index 79b70f9f3..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-->
@@ -742,12 +731,6 @@
<string name="msg_crt_warn_not_found">Ključ ni bil najden!</string>
<string name="msg_crt_warn_cert_failed">Generacija potrdila ni uspela!</string>
<!--Linked Identity verification-->
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">Del naložene datoteke je veljavnen objekt OpenPGP a ni ključ.</item>
- <item quantity="two">Deli naložene datoteke so veljavni objekti OpenPGP a niso ključi.</item>
- <item quantity="few">Deli naložene datoteke so veljavni objekti OpenPGP a niso ključi.</item>
- <item quantity="other">Deli naložene datoteke so veljavni objekti OpenPGP a niso ključi.</item>
- </plurals>
<!--Messages for Keybase Verification operation-->
<!--Messages for Mime parsing operation-->
<!--PassphraseCache-->
diff --git a/OpenKeychain/src/main/res/values-sr/strings.xml b/OpenKeychain/src/main/res/values-sr/strings.xml
index 84558d0e8..515381bc3 100644
--- a/OpenKeychain/src/main/res/values-sr/strings.xml
+++ b/OpenKeychain/src/main/res/values-sr/strings.xml
@@ -87,7 +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_copy_decrypted_text">Копирај на клипборд</string>
<string name="btn_decrypt_clipboard">Учитај са клипборда</string>
<string name="btn_decrypt_files">Изабери фајл</string>
<string name="btn_encrypt_files">Шифруј фајлове</string>
@@ -254,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>
@@ -448,6 +441,7 @@
<string name="help_tab_changelog">Дневник измена</string>
<string name="help_tab_about">О програму</string>
<string name="help_about_version">Издање:</string>
+ <string name="help_tab_donations">Донирај</string>
<!--Import-->
<string name="import_tab_keyserver">Сервер кључева</string>
<string name="import_tab_cloud">Претрага кључа</string>
@@ -685,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-->
@@ -1273,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>
@@ -1303,6 +1297,10 @@
<string name="msg_data_mime_filename">Назив фајла: „%s“</string>
<string name="msg_data_mime_from_extension">Погађам МИМЕ тип на основу екстензије</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">Рашчлањујем структуру МИМЕ података</string>
<string name="msg_data_mime_ok">Рашчлањивање завршено</string>
<string name="msg_data_mime_none">Није нађена МИМЕ структура</string>
@@ -1312,15 +1310,10 @@
<string name="msg_data_skip_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_download_no_pgp_parts">УРАДИТИ: множине!</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">део учитаног фајла је исправан ОпенПГП објекат али није ОпенПГП кључ</item>
- <item quantity="few">дела учитаног фајла су исправни ОпенПГП објекти али нису ОпенПГП кључеви</item>
- <item quantity="other">делови учитаног фајла су исправни ОпенПГП објекти али нису ОпенПГП кључеви</item>
- </plurals>
- <string name="msg_get_query_too_short">Упит за претрагу је прекратак. Прецизирајте упит!</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-->
@@ -1430,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>
@@ -1539,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>
@@ -1551,6 +1545,7 @@
<string name="snack_backup_saved">Резерва сачувана</string>
<string name="snack_backup_exists">Резерва већ постоји!</string>
<string name="snack_backup_saved_dir">Сачувано у директоријум „OpenKeychain“</string>
+ <string name="btn_backup_back">Назад на проверу</string>
<string name="snack_text_too_long">Текст је предугачак да би био приказан у целости!</string>
<string name="snack_shared_text_too_long">Дељени текст је одсечен јер је био предугачак!</string>
<string name="share_log_dialog_title">Поделити запис?</string>
@@ -1574,4 +1569,22 @@
<string name="settings_cache_ttl_at_least_one">Бар једна ставка мора бити изабрана!</string>
<string name="settings_cache_ttl_max_three">Не можете изабрати више од три ставке!</string>
<string name="remember">Запамти</string>
+ <string name="prompt_fidesmo_pgp_install_title">Инсталирати ПГП?</string>
+ <string name="prompt_fidesmo_pgp_install_message">Нема апликације за ПГП на вашем „Fidesmo“ уређају.</string>
+ <string name="prompt_fidesmo_pgp_install_button_positive">Инсталирај</string>
+ <string name="prompt_fidesmo_pgp_install_button_negative">Одустани</string>
+ <string name="prompt_fidesmo_app_install_title">Инсталирати „Fidesmo“?</string>
+ <string name="prompt_fidesmo_app_install_message">Да бисте инсталирали ПГП потребна вам је „Fidesmo“ апликација за Андроид.</string>
+ <string name="prompt_fidesmo_app_install_button_positive">Инсталирај</string>
+ <string name="prompt_fidesmo_app_install_button_negative">Одустани</string>
+ <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>
+ <item>50 евра</item>
+ <item>100 евра</item>
+ </string-array>
</resources>
diff --git a/OpenKeychain/src/main/res/values-sv/strings.xml b/OpenKeychain/src/main/res/values-sv/strings.xml
index 5ae46d286..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,13 +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_copy_decrypted_text">Kopiera dekrypterad text</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>
@@ -82,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>
@@ -95,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>
@@ -185,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>
@@ -354,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>
@@ -433,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>
@@ -515,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-->
@@ -533,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>
@@ -781,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-->
@@ -830,12 +844,6 @@
</plurals>
<!--Linked Identity verification-->
<string name="msg_acc_saved">Konto sparat</string>
- <string name="msg_get_success">Hämtning lyckades!</string>
- <string name="msg_get_no_valid_keys">Inga giltiga nycklar hittades i fil/urklipp!</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">en del av den inlästa filen är ett giltigt OpenPGP-objekt men inte en OpenPGP-nyckel</item>
- <item quantity="other">delar av den inlästa filen är giltiga OpenPGP-objekt men inte OpenPGP-nycklar</item>
- </plurals>
<!--Messages for Keybase Verification operation-->
<!--Messages for Mime parsing operation-->
<!--PassphraseCache-->
@@ -870,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>
@@ -890,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 87005eaed..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-->
@@ -387,7 +376,6 @@
<string name="msg_del_error_empty">Silinecek bir şey yok!</string>
<!--Linked Identity verification-->
<string name="msg_acc_saved">Hesap kaydedildi</string>
- <string name="msg_get_success">Başarıyla indirildi!</string>
<!--Messages for Keybase Verification operation-->
<!--Messages for Mime parsing operation-->
<!--PassphraseCache-->
diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml
index d3cef1390..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-->
@@ -475,11 +464,6 @@
<!--Messages for SignEncrypt operation-->
<!--Messages for PgpSignEncrypt operation-->
<!--Linked Identity verification-->
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">частина завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP</item>
- <item quantity="few">частини завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP</item>
- <item quantity="other">частин завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP</item>
- </plurals>
<!--Messages for Keybase Verification operation-->
<!--Messages for Mime parsing operation-->
<!--PassphraseCache-->
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 ff063b72f..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>
@@ -64,7 +65,6 @@
<string name="btn_view_cert_key">檢視簽署的金鑰</string>
<string name="btn_create_key">建立金鑰</string>
<string name="btn_add_files">加入檔案</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>
@@ -182,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>
@@ -514,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-->
@@ -660,8 +649,6 @@
<string name="msg_del_error_multi_secret">私鑰只能分別刪除!</string>
<!--Linked Identity verification-->
<string name="msg_acc_saved">帳戶已儲存</string>
- <string name="msg_get_success">下載成功!</string>
- <string name="msg_get_no_valid_keys">在檔案/剪貼簿中找不到有效的金鑰!</string>
<string name="msg_download_query_failed">查詢金鑰的時候發生錯誤。</string>
<!--Messages for Keybase Verification operation-->
<!--Messages for Mime parsing operation-->
diff --git a/OpenKeychain/src/main/res/values-zh/strings.xml b/OpenKeychain/src/main/res/values-zh/strings.xml
index 8859a6471..b29ac857b 100644
--- a/OpenKeychain/src/main/res/values-zh/strings.xml
+++ b/OpenKeychain/src/main/res/values-zh/strings.xml
@@ -13,6 +13,7 @@
<string name="title_preferences">设置</string>
<string name="title_api_registered_apps">已注册应用</string>
<string name="title_key_server_preference">OenPGP 密钥服务器</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>
@@ -31,24 +32,26 @@
<string name="title_help">帮助</string>
<string name="title_log_display">日志</string>
<string name="title_exchange_keys">交换密钥</string>
+ <string name="title_advanced_key_info">更多密钥详情</string>
<string name="title_delete_secret_key">删除你的密钥 \'%s\' ?</string>
<string name="title_manage_my_keys">管理我的密钥</string>
<!--section-->
<string name="section_user_ids">用户名</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>
+ <string name="section_should_you_trust">信任此密钥?</string>
<string name="section_proof_details">验证</string>
<string name="section_keys">子密钥</string>
<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">密码和 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>
<string name="section_sync_settings">同步</string>
- <string name="section_sync_settings_summary">密钥自动更新,contact linking</string>
+ <string name="section_sync_settings_summary">密钥自动更新,关联联系人</string>
<string name="section_experimental_features">试验性功能</string>
<string name="section_certify">确认</string>
<string name="section_actions">动作</string>
@@ -82,10 +85,10 @@
<string name="btn_paste_encrypted_signed">加密/签名并粘贴文本</string>
<string name="btn_view_cert_key">显示密钥</string>
<string name="btn_create_key">创建密钥</string>
- <string name="btn_add_files">添加文件(多个)</string>
+ <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_copy_decrypted_text">复制到剪贴板</string>
<string name="btn_decrypt_clipboard">从剪贴板导入</string>
<string name="btn_decrypt_files">选择导入文件</string>
<string name="btn_encrypt_files">加密文件</string>
@@ -100,7 +103,7 @@
<string name="cd_encrypt_files">加密文件</string>
<string name="cd_exchange_keys">交换密钥</string>
<string name="cd_encrypt_text">加密文本</string>
- <string name="cd_share_nfc">使用NFC分享</string>
+ <string name="cd_share_nfc">使用 NFC 分享</string>
<!--menu-->
<string name="menu_preferences">参数设置</string>
<string name="menu_help">帮助</string>
@@ -108,12 +111,13 @@
<string name="menu_delete_key">删除密钥</string>
<string name="menu_manage_keys">管理我的密钥</string>
<string name="menu_search">搜索</string>
- <string name="menu_nfc_preferences">NFC设置</string>
+ <string name="menu_nfc_preferences">NFC 设置</string>
<string name="menu_beam_preferences">参数</string>
<string name="menu_encrypt_to">加密到...</string>
<string name="menu_select_all">选择全部</string>
<string name="menu_export_all_keys">导出全部密钥</string>
<string name="menu_update_all_keys">更新所有密钥</string>
+ <string name="menu_advanced">更多密钥详情</string>
<string name="menu_certify_fingerprint">通过指纹确认</string>
<string name="menu_certify_fingerprint_phrases">通过短语认证</string>
<string name="menu_share_log">分享记录</string>
@@ -135,6 +139,7 @@
<string name="label_file_ascii_armor">启用ASCII文本化</string>
<string name="label_write_version_header">写入文件头信息</string>
<string name="label_write_version_header_summary">在OpenPGP签名、加密文本和导出密钥中写入 \'OpenKeychain v2.7\' 标记。</string>
+ <string name="label_use_num_keypad_for_security_token_pin">使用数字键盘输入安全令牌识别码</string>
<string name="label_asymmetric_from">签名为:</string>
<string name="label_to">加密到</string>
<string name="label_delete_after_encryption">加密文件后删除</string>
@@ -142,11 +147,12 @@
<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>
- <string name="label_keyservers">选择OpenPGP 密钥服务器</string>
- <string name="label_key_id">密钥ID</string>
+ <string name="label_keyservers">选择 OpenPGP 密钥服务器</string>
+ <string name="label_key_id">密钥 ID</string>
<string name="label_key_created">创建密钥 %s</string>
<string name="label_creation">创建</string>
<string name="label_expiry">过期时间</string>
@@ -173,16 +179,17 @@
<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_keyserver_summary">在指定的 OpenPGP 密钥服务器上搜索(HKP协议)</string>
<string name="pref_keybase">keybase.io</string>
- <string name="pref_keybase_summary">在keybase.io搜索密钥</string>
+ <string name="pref_keybase_summary">在 keybase.io 搜索密钥</string>
<string name="pref_facebook">脸书</string>
<string name="pref_facebook_summary">根据用户名在脸书上搜索密钥</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">仅在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_on">根据名字和 email 地址将密钥关联到联系人。这是本地操作,不需要联网。</string>
<string name="label_sync_settings_contacts_summary_off">新密钥将不关联到联系方式</string>
<!--label shown in Android settings under the OpenKeychain account-->
<string name="keyserver_sync_settings_title">自动更新密钥</string>
@@ -194,10 +201,10 @@
<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>
+ <string name="label_experimental_settings_theme_summary">(图标和某些界面还未根据深色主题进行调整)</string>
<!--Proxy Preferences-->
- <string name="pref_proxy_tor_title">启用Tor代理</string>
- <string name="pref_proxy_tor_summary">必需已安装Orbot</string>
+ <string name="pref_proxy_tor_title">启用 Tor 代理</string>
+ <string name="pref_proxy_tor_summary">必需已安装 Orbot</string>
<string name="pref_proxy_normal_title">启用其它代理服务器</string>
<string name="pref_proxy_host_title">服务器地址</string>
<string name="pref_proxy_host_err_invalid">服务器地址不能为空</string>
@@ -208,16 +215,16 @@
<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">不使用Tor代理</string>
+ <string name="orbot_ignore_tor">不使用 Tor 代理</string>
<!--InstallDialogFragment strings-->
- <string name="orbot_install_dialog_title">为了使用Tor代理而安装Orbot?</string>
+ <string name="orbot_install_dialog_title">安装 Orbot 以使用 Tor 代理?</string>
<string name="orbot_install_dialog_install">安装</string>
- <string name="orbot_install_dialog_content">必需安装Orbot并启用代理传输.你希望安装么?</string>
+ <string name="orbot_install_dialog_content">必需安装 Orbot 并启用代理传输。您希望安装么?</string>
<string name="orbot_install_dialog_cancel">取消</string>
- <string name="orbot_install_dialog_ignore_tor">不使用Tor</string>
+ <string name="orbot_install_dialog_ignore_tor">不使用 Tor</string>
<!--StartOrbotDialogFragment strings-->
- <string name="orbot_start_dialog_title">运行Orbot?</string>
- <string name="orbot_start_dialog_content">Orbot未运行。你希望打开并连接Tor?</string>
+ <string name="orbot_start_dialog_title">运行 Orbot ?</string>
+ <string name="orbot_start_dialog_content">Orbot 未运行。你希望打开并连接 Tor ?</string>
<string name="orbot_start_btn">打开Orbot</string>
<string name="orbot_start_dialog_start">运行Orbot</string>
<string name="orbot_start_dialog_cancel">取消</string>
@@ -246,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>
@@ -268,8 +265,13 @@
<string name="passphrase_must_not_be_empty">请输入一个密码</string>
<string name="passphrase_for_symmetric_encryption">输入密码</string>
<string name="passphrase_for_backup">输入备份码</string>
- <string name="passphrase_for">输入密码为 \'%s\'</string>
- <string name="pin_for">输入PIN码为 \'%s\'</string>
+ <string name="passphrase_for">为 \'%s\' 输入密码</string>
+ <string name="pin_for">为 \'%s\' 输入PIN码</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">共%2$d个文件 %1$d 个文件已删除.%3$s</string>
@@ -293,9 +295,9 @@
<string name="keys_exported">成功导出多个密钥</string>
<string name="no_keys_exported">没有密钥被导出</string>
<string name="key_creation_el_gamal_info">注意: 只有公钥支持ElGamal。</string>
- <string name="key_not_found">没有找到密钥</string>
+ <string name="key_not_found">没有找到密钥 %08X</string>
<plurals name="bad_keys_encountered">
- <item quantity="other">其他</item>
+ <item quantity="other">忽略了 %d 个无效密钥。您也许在导出密钥时使用了选项\n --export-secret-subkeys\n请确认您是用\n --export-secret-keys\n选项导出密钥。</item>
</plurals>
<string name="list_empty">这个列表是空的!</string>
<string name="nfc_successful">成功通过NFC发送密钥</string>
@@ -325,7 +327,7 @@
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">使用Android\'s NFC Beam功能需要Android 4.1及以上</string>
<string name="error_nfc_needed">必须开启NFC!</string>
- <string name="error_beam_needed">Bean 必须开启!</string>
+ <string name="error_beam_needed">Beam 必须开启!</string>
<string name="error_nothing_import">未发现密钥!</string>
<string name="error_nothing_import_selected">未选择导入的密钥!</string>
<string name="error_contacts_key_id_missing">从联系人取回密钥ID失败!</string>
@@ -395,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-->
@@ -436,6 +438,7 @@
<string name="help_tab_changelog">更新日志</string>
<string name="help_tab_about">关于</string>
<string name="help_about_version">版本:</string>
+ <string name="help_tab_donations">捐赠</string>
<!--Import-->
<string name="import_tab_keyserver">密钥服务器</string>
<string name="import_tab_cloud">搜索密钥</string>
@@ -510,6 +513,8 @@
<string name="intent_send_encrypt">用 OpenKeychain 加密文件</string>
<string name="intent_send_decrypt">用 OpenKeychain 解密</string>
<!--Remote API-->
+ <string name="api_settings_show_info">显示高级信息</string>
+ <string name="api_settings_hide_info">隐藏高级信息</string>
<string name="api_settings_no_key">尚未选择密钥</string>
<string name="api_settings_select_key">选择密钥</string>
<string name="api_settings_create_key">创建密钥</string>
@@ -522,6 +527,7 @@
<string name="api_settings_package_name">包名字</string>
<string name="api_settings_package_certificate">证书的指纹(sha256)</string>
<string name="api_settings_accounts">账户(使用旧的应用接口)</string>
+ <string name="api_settings_advanced">更多密钥详情</string>
<string name="api_settings_allowed_keys">允许的密钥</string>
<string name="api_settings_settings">设置</string>
<string name="api_settings_key">账户密钥</string>
@@ -580,6 +586,7 @@
<string name="key_view_action_share_nfc">通过NFC分享</string>
<string name="key_view_action_upload">上传至服务器</string>
<string name="key_view_tab_main">主要信息</string>
+ <string name="key_view_tab_start">开始</string>
<string name="key_view_tab_share">分享</string>
<string name="key_view_tab_keys">子密钥</string>
<string name="key_view_tab_certs">证书列表</string>
@@ -637,10 +644,17 @@
</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_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_empty">该内容是必须的</string>
@@ -697,6 +711,8 @@
<!--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>
<string name="cert_verify_failed">失败!</string>
@@ -708,6 +724,7 @@
<!--Import Public log entries-->
<string name="msg_ip_apply_batch">开始批量导入</string>
<string name="msg_ip_bad_type_secret">已将私钥导入为公钥,这是一个BUG,请提交报告!</string>
+ <string name="msg_ip_delete_old_fail">没有旧密钥被删除(创建一个新密钥?)</string>
<string name="msg_ip_delete_old_ok">已从数据库中删除旧的密钥</string>
<string name="msg_ip_encode_fail">由于编码错误,操作失败</string>
<string name="msg_ip_error_io_exc">由于i/o异常,操作失败</string>
@@ -723,22 +740,77 @@
<string name="msg_ip_master_expired">钥匙环过期于 %s</string>
<string name="msg_ip_master_expires">钥匙环过期于 %s</string>
<string name="msg_ip_master_flags_unspecified">主标志:未指定的(假定全部 )</string>
+ <string name="msg_ip_master_flags_cesa">主标志:证书,加密,签名,验证</string>
+ <string name="msg_ip_master_flags_cesx">主标志:证书,加密,签名</string>
+ <string name="msg_ip_master_flags_cexa">主标志:证书,加密,验证</string>
+ <string name="msg_ip_master_flags_cexx">主标志:证书,加密</string>
+ <string name="msg_ip_master_flags_cxsa">主标志:证书,签名,验证</string>
+ <string name="msg_ip_master_flags_cxsx">主标志:证书,签名,验证</string>
+ <string name="msg_ip_master_flags_cxxa">主标志:证书,验证</string>
+ <string name="msg_ip_master_flags_cxxx">主标志:证书</string>
+ <string name="msg_ip_master_flags_xesa">主标志:加密,签名,验证</string>
+ <string name="msg_ip_master_flags_xesx">主标志:加密,签名</string>
+ <string name="msg_ip_master_flags_xexa">主标志:加密,验证</string>
+ <string name="msg_ip_master_flags_xexx">主标志:加密</string>
+ <string name="msg_ip_master_flags_xxsa">主标志:签名,验证</string>
+ <string name="msg_ip_master_flags_xxsx">主标志:签名</string>
+ <string name="msg_ip_master_flags_xxxa">主标志:验证</string>
+ <string name="msg_ip_master_flags_xxxx">主标志:无</string>
<string name="msg_ip_merge_public">正在合并已导入的数据到现有的公共钥匙环</string>
<string name="msg_ip_merge_secret">正在合并已导入的数据到现有的私有钥匙环</string>
<string name="msg_ip_subkey">正在处理子密钥 %s</string>
<string name="msg_ip_subkey_expired">子密钥过期于 %s</string>
<string name="msg_ip_subkey_expires">子密钥过期于 %s</string>
+ <string name="msg_ip_subkey_flags_unspecified">子密钥标志:未指定的(假设所有)</string>
+ <string name="msg_ip_subkey_flags_cesa">子密钥标志:证书,加密,签名,验证</string>
+ <string name="msg_ip_subkey_flags_cesx">子密钥标志:证书,加密,签名</string>
+ <string name="msg_ip_subkey_flags_cexa">子密钥标志:证书,加密,验证</string>
+ <string name="msg_ip_subkey_flags_cexx">子密钥标志:证书,加密</string>
+ <string name="msg_ip_subkey_flags_cxsa">子密钥标志:证书,签名,验证</string>
+ <string name="msg_ip_subkey_flags_cxsx">子密钥标志:证书,签名</string>
+ <string name="msg_ip_subkey_flags_cxxa">子密钥标志:证书,验证</string>
+ <string name="msg_ip_subkey_flags_cxxx">子密钥标志:证书</string>
+ <string name="msg_ip_subkey_flags_xesa">子密钥标志:加密,签名,验证</string>
+ <string name="msg_ip_subkey_flags_xesx">子密钥标志:加密,签名</string>
+ <string name="msg_ip_subkey_flags_xexa">子密钥标志:加密,验证</string>
+ <string name="msg_ip_subkey_flags_xexx">子密钥标志:加密</string>
+ <string name="msg_ip_subkey_flags_xxsa">子密钥标志:签名,验证</string>
+ <string name="msg_ip_subkey_flags_xxsx">子密钥标志:签名</string>
+ <string name="msg_ip_subkey_flags_xxxa">子密钥标志:验证</string>
+ <string name="msg_ip_subkey_flags_xxxx">子密钥标志:无</string>
<string name="msg_ip_success">成功导入公共钥匙环</string>
<string name="msg_ip_success_identical">钥匙环不包含任何新的数据,无动作</string>
<string name="msg_ip_reinsert_secret">重新插入私钥</string>
<string name="msg_ip_uid_cert_bad">发现损坏的证书!</string>
+ <string name="msg_ip_uid_cert_error">证书处理错误</string>
+ <string name="msg_ip_uid_cert_nonrevoke">已有一个未吊销的证书,跳过</string>
+ <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>
@@ -746,19 +818,50 @@
<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)-->
+ <string name="msg_ed_error_key_not_found">未找到密钥</string>
<!--Promote key-->
+ <string name="msg_pr_error_key_not_found">未找到密钥</string>
<!--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>
@@ -772,13 +875,17 @@
<string name="backup_section">备份</string>
<string name="restore_section">恢复</string>
<!--unsorted-->
+ <string name="error_key_not_found">未找到密钥</string>
<string name="user_id_none"><![CDATA[<none>]]></string>
<!--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 9920d8057..2a44307f8 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,10 @@
<!-- 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_all_keys_stripped">All keys are stripped!</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>
@@ -1065,6 +1078,7 @@
<string name="msg_mf_error_null_expiry">"Expiry time cannot be "same as before" on subkey creation. This is a programming error, please file a bug report!"</string>
<string name="msg_mf_error_noop">"Nothing to do!"</string>
<string name="msg_mf_error_passphrase_master">"Fatal error decrypting master key! This is likely a programming error, please file a bug report!"</string>
+ <string name="msg_mf_error_passphrases_unchanged">"No password changed!"</string>
<string name="msg_mf_error_pgp">"Internal OpenPGP error!"</string>
<string name="msg_mf_error_sig">"Signature exception!"</string>
<string name="msg_mf_error_sub_stripped">"Cannot modify stripped subkey %s!"</string>
@@ -1085,7 +1099,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 +1195,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>
@@ -1249,6 +1263,7 @@
<string name="msg_pse_error_pgp">"Internal OpenPGP error!"</string>
<string name="msg_pse_error_sig">"Encountered OpenPGP signature exception!"</string>
<string name="msg_pse_error_unlock">"Unknown error unlocking key!"</string>
+ <string name="msg_pse_error_revoked_or_expired">"Revoked/Expired key cannot be used for sign or encryption"</string>
<string name="msg_pse_key_ok">"Encrypting for key: %s"</string>
<string name="msg_pse_key_unknown">"Missing key for encryption: %s"</string>
<string name="msg_pse_key_warn">"Bad key for encryption: %s"</string>
@@ -1486,7 +1501,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 +1559,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>
@@ -1563,17 +1578,17 @@
<item quantity="other">"Incorrect PIN!\n%d tries remaining."</item>
</plurals>
<string name="security_token_error_terminated">"Security Token in termination state."</string>
- <string name="security_token_error_wrong_length">"Entered PIN is too short. PINs are at least 6 digits long."</string>
+ <string name="security_token_error_wrong_length">"Entered PIN is too short. PINs are at least 6 digits long.\n(Error: Wrong length)"</string>
<string name="security_token_error_conditions_not_satisfied">"Conditions of use not satisfied."</string>
- <string name="security_token_error_security_not_satisfied">"Security status not satisfied."</string>
+ <string name="security_token_error_security_not_satisfied">"Incorrect PIN!\n(Keep in mind that you have only 3 tries!)"</string>
<string name="security_token_error_authentication_blocked">"PIN blocked after too many attempts."</string>
<string name="security_token_error_data_not_found">"Key or object not found."</string>
<string name="security_token_error_unknown">"Unknown Error"</string>
- <string name="security_token_error_bad_data">"Security Token reported invalid data."</string>
+ <string name="security_token_error_bad_data">"Entered PIN is too short. PINs are at least 6 digits long.\n(Error: Invalid data)"</string>
<string name="security_token_error_chaining_error">"Security Token expected last command in a chain."</string>
<string name="security_token_error_header">"Security Token reported invalid %s byte."</string>
<string name="security_token_error_tag_lost">"Security Token has been taken off too early. Keep the Security Token at the back until the operation finishes."</string>
- <string name="security_token_error_iso_dep_not_supported">"Tag does not support ISO-DEP (ISO 14443-4)"</string>
+ <string name="security_token_error_iso_dep_not_supported">"Security Token does not support the required communication standard (ISO-DEP, ISO 14443-4)"</string>
<string name="security_token_error_try_again">"Try again"</string>
<string name="btn_delete_original">Delete original file</string>
@@ -1624,7 +1639,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 +1662,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>
@@ -1733,10 +1748,16 @@
<string name="help_donation_paypal_item">OpenKeychain Donation</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>
+ <string name="redirect_import_key_title">"Scan with OpenKeychain!"</string>
+ <string name="redirect_import_key_message">"You scanned a OpenPGP QR Code with another app, please scan with Openkeychain directly to be safe."</string>
+ <string name="redirect_import_key_yes">"Scan again"</string>
+ <string name="redirect_import_key_no">"Close"</string>
+ <string name="title_activity_redirect_key">"Key import redirection"</string>
</resources>
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/BenchmarkOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/BenchmarkOperationTest.java
index 175b7687d..381c7a490 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/BenchmarkOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/BenchmarkOperationTest.java
@@ -50,7 +50,6 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
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.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
index 726365f00..94d26a7d1 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
@@ -41,9 +41,9 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
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.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -84,7 +84,7 @@ public class CertifyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("derp");
- parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase1);
+ parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase1));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success());
@@ -108,7 +108,7 @@ public class CertifyOperationTest {
parcel.mAddUserAttribute.add(
WrappedUserAttribute.fromSubpacket(random.nextInt(100)+1, uatdata));
- parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase2);
+ parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase2));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success());
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 0698f4fb7..6fab979ed 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;
@@ -57,9 +57,9 @@ import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.TemporaryFileProvider;
import org.sufficientlysecure.keychain.service.BackupKeyringParcel;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
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.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Passphrase;
@@ -106,7 +106,7 @@ public class ExportTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("snips");
- parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase1);
+ parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase1));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
assertTrue("initial test key creation must succeed", result.success());
@@ -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.setNewUnlock(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/operations/PromoteKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
index 442e252af..98f9fe482 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
@@ -45,10 +45,10 @@ import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -80,7 +80,7 @@ public class PromoteKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("derp");
- parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase1);
+ parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase1));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success());
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
index bcbe1b8d6..3770b825c 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
@@ -54,9 +54,9 @@ import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
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.SubkeyChange;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
@@ -103,7 +103,7 @@ public class PgpEncryptDecryptTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("bloom");
- parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase1);
+ parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase1));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success());
@@ -121,7 +121,7 @@ public class PgpEncryptDecryptTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("belle");
- parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase2);
+ parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase2));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success());
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..03b923d90 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
@@ -45,10 +45,9 @@ 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.ChangeUnlockParcel;
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.SubkeyAdd;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
@@ -110,7 +109,7 @@ public class PgpKeyOperationTest {
parcel.mAddUserAttribute.add(uat);
}
- parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
+ parcel.setNewUnlock(new ChangeUnlockParcel(passphrase));
PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
@@ -150,7 +149,7 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, new Random().nextInt(256)+255, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy");
- parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
+ parcel.setNewUnlock(new ChangeUnlockParcel(passphrase));
assertFailure("creating ring with < 2048 bit keysize should fail", parcel,
LogType.MSG_CR_ERROR_KEYSIZE_2048);
@@ -161,7 +160,7 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ELGAMAL, 2048, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy");
- parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
+ parcel.setNewUnlock(new ChangeUnlockParcel(passphrase));
assertFailure("creating ring with ElGamal master key should fail", parcel,
LogType.MSG_CR_ERROR_FLAGS_ELGAMAL);
@@ -172,7 +171,7 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, null));
parcel.mAddUserIds.add("lotus");
- parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
+ parcel.setNewUnlock(new ChangeUnlockParcel(passphrase));
assertFailure("creating master key with null expiry should fail", parcel,
LogType.MSG_CR_ERROR_NULL_EXPIRY);
@@ -183,7 +182,7 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("shy");
- parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
+ parcel.setNewUnlock(new ChangeUnlockParcel(passphrase));
assertFailure("creating ring with non-certifying master key should fail", parcel,
LogType.MSG_CR_ERROR_NO_CERTIFY);
@@ -193,7 +192,7 @@ public class PgpKeyOperationTest {
parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
- parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
+ parcel.setNewUnlock(new ChangeUnlockParcel(passphrase));
assertFailure("creating ring without user ids should fail", parcel,
LogType.MSG_CR_ERROR_NO_USER_ID);
@@ -202,7 +201,7 @@ public class PgpKeyOperationTest {
{
parcel.reset();
parcel.mAddUserIds.add("shy");
- parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
+ parcel.setNewUnlock(new ChangeUnlockParcel(passphrase));
assertFailure("creating ring with no master key should fail", parcel,
LogType.MSG_CR_ERROR_NO_MASTER);
@@ -842,7 +841,7 @@ public class PgpKeyOperationTest {
parcelKey.mAddUserIds.add("yubikey");
- parcelKey.mNewUnlock = new ChangeUnlockParcel(passphrase);
+ parcelKey.setNewUnlock(new ChangeUnlockParcel(passphrase));
PgpKeyOperation opSecurityToken = new PgpKeyOperation(null);
PgpEditKeyResult resultSecurityToken = opSecurityToken.createSecretKeyRing(parcelKey);
@@ -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);
}
}
@@ -1157,7 +1156,7 @@ public class PgpKeyOperationTest {
public void testPassphraseChange() throws Exception {
// change passphrase to empty
- parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
+ parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase()));
// note that canonicalization here necessarily strips the empty notation packet
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, cryptoInput);
@@ -1172,7 +1171,7 @@ public class PgpKeyOperationTest {
// modify keyring, change to non-empty passphrase
Passphrase otherPassphrase = TestingUtils.genPassphrase(true);
CryptoInputParcel otherCryptoInput = new CryptoInputParcel(otherPassphrase);
- parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase);
+ parcel.setNewUnlock(new ChangeUnlockParcel(otherPassphrase));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB,
new CryptoInputParcel(new Date(), new Passphrase()));
@@ -1198,7 +1197,7 @@ public class PgpKeyOperationTest {
PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag);
Passphrase otherPassphrase2 = TestingUtils.genPassphrase(true);
- parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase2);
+ parcel.setNewUnlock(new ChangeUnlockParcel(otherPassphrase2));
{
// if we replace a secret key with one without passphrase
modified = KeyringTestingHelper.removePacket(modified, sKeyNoPassphrase.position);
@@ -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/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java
index df92547fe..de3ae2941 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java
@@ -58,11 +58,11 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
@@ -117,7 +117,7 @@ public class UncachedKeyringCanonicalizeTest {
}
// passphrase is tested in PgpKeyOperationTest, just use empty here
- parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
+ parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase()));
PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java
index 0878d20aa..a8e28a7c6 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java
@@ -41,9 +41,9 @@ import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation.PgpCertifyResult;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
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.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
@@ -114,7 +114,7 @@ public class UncachedKeyringMergeTest {
}
// passphrase is tested in PgpKeyOperationTest, just use empty here
- parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
+ parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase()));
PgpKeyOperation op = new PgpKeyOperation(null);
OperationResult.OperationLog log = new OperationResult.OperationLog();
@@ -131,7 +131,7 @@ public class UncachedKeyringMergeTest {
parcel.mAddUserIds.add("shy");
// passphrase is tested in PgpKeyOperationTest, just use empty here
- parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
+ parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase()));
PgpKeyOperation op = new PgpKeyOperation(null);
OperationResult.OperationLog log = new OperationResult.OperationLog();
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java
index a185bdebf..ef82aa527 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java
@@ -34,9 +34,9 @@ import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.ByteArrayInputStream;
@@ -78,7 +78,7 @@ public class UncachedKeyringTest {
parcel.mAddUserAttribute.add(uat);
}
// passphrase is tested in PgpKeyOperationTest, just use empty here
- parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
+ parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase()));
PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
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 29a7c77e4757239ae861792dd51d75987940f4e
+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