Merge branch 'development' into linked-identities
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@ -35,6 +35,7 @@ python copy OpenKeychain navigation grey close 24
|
||||
python copy OpenKeychain social grey person 24
|
||||
python copy OpenKeychain social grey person_add 24
|
||||
python copy OpenKeychain social grey share 24
|
||||
python copy OpenKeychain communication grey vpn_key 24
|
||||
|
||||
# navigation drawer sections
|
||||
python copy OpenKeychain communication black vpn_key 24
|
||||
|
@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
|
||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
|
||||
@ -54,14 +55,15 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.security.Security;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
|
||||
public class CertifyOperationTest {
|
||||
|
||||
static String mPassphrase = TestingUtils.genPassphrase(true);
|
||||
|
||||
static UncachedKeyRing mStaticRing1, mStaticRing2;
|
||||
static String mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||
static String mKeyPhrase2 = TestingUtils.genPassphrase(true);
|
||||
@ -74,6 +76,8 @@ public class CertifyOperationTest {
|
||||
oldShadowStream = ShadowLog.stream;
|
||||
// ShadowLog.stream = System.out;
|
||||
|
||||
Random random = new Random();
|
||||
|
||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||
|
||||
{
|
||||
@ -102,8 +106,14 @@ public class CertifyOperationTest {
|
||||
Algorithm.DSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
|
||||
|
||||
parcel.mAddUserIds.add("ditz");
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(null, "1234");
|
||||
byte[] uatdata = new byte[random.nextInt(150)+10];
|
||||
random.nextBytes(uatdata);
|
||||
parcel.mAddUserAttribute.add(
|
||||
WrappedUserAttribute.fromSubpacket(random.nextInt(100)+1, uatdata));
|
||||
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase2);
|
||||
|
||||
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
|
||||
Assert.assertTrue("initial test key creation must succeed", result.success());
|
||||
@ -140,7 +150,7 @@ public class CertifyOperationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCertify() throws Exception {
|
||||
public void testCertifyId() throws Exception {
|
||||
CertifyOperation op = operationWithFakePassphraseCache(
|
||||
mStaticRing1.getMasterKeyId(), mStaticRing1.getMasterKeyId(), mKeyPhrase1);
|
||||
|
||||
@ -152,7 +162,8 @@ public class CertifyOperationTest {
|
||||
}
|
||||
|
||||
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
|
||||
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId()));
|
||||
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(),
|
||||
mStaticRing2.getPublicKey().getUnorderedUserIds()));
|
||||
CertifyResult result = op.certify(actions, null);
|
||||
|
||||
Assert.assertTrue("certification must succeed", result.success());
|
||||
@ -166,13 +177,43 @@ public class CertifyOperationTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCertifyAttribute() throws Exception {
|
||||
CertifyOperation op = operationWithFakePassphraseCache(
|
||||
mStaticRing1.getMasterKeyId(), mStaticRing1.getMasterKeyId(), mKeyPhrase1);
|
||||
|
||||
{
|
||||
CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
|
||||
.getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId());
|
||||
Assert.assertEquals("public key must not be marked verified prior to certification",
|
||||
Certs.UNVERIFIED, ring.getVerified());
|
||||
}
|
||||
|
||||
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
|
||||
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(), null,
|
||||
mStaticRing2.getPublicKey().getUnorderedUserAttributes()));
|
||||
CertifyResult result = op.certify(actions, null);
|
||||
|
||||
Assert.assertTrue("certification must succeed", result.success());
|
||||
|
||||
{
|
||||
CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
|
||||
.getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId());
|
||||
Assert.assertEquals("new key must be verified now",
|
||||
Certs.VERIFIED_SECRET, ring.getVerified());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCertifySelf() throws Exception {
|
||||
CertifyOperation op = operationWithFakePassphraseCache(
|
||||
mStaticRing1.getMasterKeyId(), mStaticRing1.getMasterKeyId(), mKeyPhrase1);
|
||||
|
||||
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
|
||||
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId()));
|
||||
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(),
|
||||
mStaticRing2.getPublicKey().getUnorderedUserIds()));
|
||||
|
||||
CertifyResult result = op.certify(actions, null);
|
||||
|
||||
@ -188,7 +229,9 @@ public class CertifyOperationTest {
|
||||
|
||||
{
|
||||
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
|
||||
actions.add(new CertifyAction(1234L));
|
||||
ArrayList<String> uids = new ArrayList<String>();
|
||||
uids.add("nonexistent");
|
||||
actions.add(new CertifyAction(1234L, uids));
|
||||
|
||||
CertifyResult result = op.certify(actions, null);
|
||||
|
||||
@ -199,7 +242,8 @@ public class CertifyOperationTest {
|
||||
|
||||
{
|
||||
CertifyActionsParcel actions = new CertifyActionsParcel(1234L);
|
||||
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId()));
|
||||
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(),
|
||||
mStaticRing2.getPublicKey().getUnorderedUserIds()));
|
||||
|
||||
CertifyResult result = op.certify(actions, null);
|
||||
|
||||
|
@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.io.File;
|
||||
@ -26,6 +28,8 @@ import java.io.File;
|
||||
public final class Constants {
|
||||
|
||||
public static final boolean DEBUG = BuildConfig.DEBUG;
|
||||
public static final boolean DEBUG_LOG_DB_QUERIES = false;
|
||||
public static final boolean DEBUG_SYNC_REMOVE_CONTACTS = false;
|
||||
|
||||
public static final String TAG = "Keychain";
|
||||
|
||||
@ -60,24 +64,17 @@ public final class Constants {
|
||||
}
|
||||
|
||||
public static final class Pref {
|
||||
public static final String DEFAULT_ENCRYPTION_ALGORITHM = "defaultEncryptionAlgorithm";
|
||||
public static final String DEFAULT_HASH_ALGORITHM = "defaultHashAlgorithm";
|
||||
public static final String DEFAULT_ASCII_ARMOR = "defaultAsciiArmor";
|
||||
public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression";
|
||||
public static final String DEFAULT_FILE_COMPRESSION = "defaultFileCompression";
|
||||
public static final String PASSPHRASE_CACHE_TTL = "passphraseCacheTtl";
|
||||
public static final String PASSPHRASE_CACHE_SUBS = "passphraseCacheSubs";
|
||||
public static final String LANGUAGE = "language";
|
||||
public static final String KEY_SERVERS = "keyServers";
|
||||
public static final String PREF_DEFAULT_VERSION = "keyServersDefaultVersion";
|
||||
public static final String WRITE_VERSION_HEADER = "writeVersionHeader";
|
||||
public static final String FIRST_TIME = "firstTime";
|
||||
public static final String SHOW_ADVANCED_TABS = "showAdvancedTabs";
|
||||
public static final String CACHED_CONSOLIDATE = "cachedConsolidate";
|
||||
public static final String SEARCH_KEYSERVER = "search_keyserver_pref";
|
||||
public static final String SEARCH_KEYBASE = "search_keybase_pref";
|
||||
public static final String USE_DEFAULT_YUBIKEY_PIN = "useDefaultYubikeyPin";
|
||||
public static final String USE_NUMKEYPAD_FOR_YUBIKEY_PIN="useNumKeypadForYubikeyPin";
|
||||
public static final String USE_NUMKEYPAD_FOR_YUBIKEY_PIN = "useNumKeypadForYubikeyPin";
|
||||
}
|
||||
|
||||
public static final class Defaults {
|
||||
@ -89,4 +86,5 @@ public final class Constants {
|
||||
public static final int none = 0;
|
||||
public static final int symmetric = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -101,18 +101,26 @@ public class CertifyOperation extends BaseOperation {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action.mUserIds == null) {
|
||||
log.add(LogType.MSG_CRT_CERTIFY_ALL, 2,
|
||||
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
|
||||
} else {
|
||||
log.add(LogType.MSG_CRT_CERTIFY_SOME, 2, action.mUserIds.size(),
|
||||
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
|
||||
}
|
||||
|
||||
CanonicalizedPublicKeyRing publicRing =
|
||||
mProviderHelper.getCanonicalizedPublicKeyRing(action.mMasterKeyId);
|
||||
|
||||
UncachedKeyRing certifiedKey = certificationKey.certifyUserIds(publicRing, action.mUserIds, null, null);
|
||||
UncachedKeyRing certifiedKey = null;
|
||||
if (action.mUserIds != null) {
|
||||
log.add(LogType.MSG_CRT_CERTIFY_UIDS, 2, action.mUserIds.size(),
|
||||
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
|
||||
|
||||
certifiedKey = certificationKey.certifyUserIds(
|
||||
publicRing, action.mUserIds, null, null);
|
||||
}
|
||||
|
||||
if (action.mUserAttributes != null) {
|
||||
log.add(LogType.MSG_CRT_CERTIFY_UATS, 2, action.mUserAttributes.size(),
|
||||
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
|
||||
|
||||
certifiedKey = certificationKey.certifyUserAttributes(
|
||||
publicRing, action.mUserAttributes, null, null);
|
||||
}
|
||||
|
||||
if (certifiedKey == null) {
|
||||
certifyError += 1;
|
||||
log.add(LogType.MSG_CRT_WARN_CERT_FAILED, 3);
|
||||
|
@ -505,10 +505,6 @@ public class ImportExportOperation extends BaseOperation {
|
||||
// Create an output stream
|
||||
try {
|
||||
arOutStream = new ArmoredOutputStream(outStream);
|
||||
String version = PgpHelper.getVersionForHeader(mContext);
|
||||
if (version != null) {
|
||||
arOutStream.setHeader("Version", version);
|
||||
}
|
||||
|
||||
log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId));
|
||||
|
||||
@ -533,10 +529,6 @@ public class ImportExportOperation extends BaseOperation {
|
||||
if (exportSecret && cursor.getInt(3) > 0) {
|
||||
try {
|
||||
arOutStream = new ArmoredOutputStream(outStream);
|
||||
String version = PgpHelper.getVersionForHeader(mContext);
|
||||
if (version != null) {
|
||||
arOutStream.setHeader("Version", version);
|
||||
}
|
||||
|
||||
// export secret key part
|
||||
log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId(keyId));
|
||||
|
@ -626,6 +626,7 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_DC_ERROR_NO_DATA (LogLevel.ERROR, R.string.msg_dc_error_no_data),
|
||||
MSG_DC_ERROR_NO_KEY (LogLevel.ERROR, R.string.msg_dc_error_no_key),
|
||||
MSG_DC_ERROR_PGP_EXCEPTION (LogLevel.ERROR, R.string.msg_dc_error_pgp_exception),
|
||||
MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO (LogLevel.ERROR, R.string.msg_dc_error_unsupported_hash_algo),
|
||||
MSG_DC_INTEGRITY_CHECK_OK (LogLevel.INFO, R.string.msg_dc_integrity_check_ok),
|
||||
MSG_DC_OK_META_ONLY (LogLevel.OK, R.string.msg_dc_ok_meta_only),
|
||||
MSG_DC_OK (LogLevel.OK, R.string.msg_dc_ok),
|
||||
@ -640,6 +641,7 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_DC_TRAIL_SYM (LogLevel.DEBUG, R.string.msg_dc_trail_sym),
|
||||
MSG_DC_TRAIL_UNKNOWN (LogLevel.DEBUG, R.string.msg_dc_trail_unknown),
|
||||
MSG_DC_UNLOCKING (LogLevel.INFO, R.string.msg_dc_unlocking),
|
||||
MSG_DC_OLD_SYMMETRIC_ENCRYPTION_ALGO (LogLevel.WARN, R.string.msg_dc_old_symmetric_encryption_algo),
|
||||
|
||||
// verify signed literal data
|
||||
MSG_VL (LogLevel.INFO, R.string.msg_vl),
|
||||
@ -689,8 +691,8 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_PSE_SYMMETRIC (LogLevel.INFO, R.string.msg_pse_symmetric),
|
||||
|
||||
MSG_CRT_CERTIFYING (LogLevel.DEBUG, R.string.msg_crt_certifying),
|
||||
MSG_CRT_CERTIFY_ALL (LogLevel.DEBUG, R.string.msg_crt_certify_all),
|
||||
MSG_CRT_CERTIFY_SOME (LogLevel.DEBUG, R.plurals.msg_crt_certify_some),
|
||||
MSG_CRT_CERTIFY_UIDS (LogLevel.DEBUG, R.plurals.msg_crt_certify_uids),
|
||||
MSG_CRT_CERTIFY_UATS (LogLevel.DEBUG, R.plurals.msg_crt_certify_uats),
|
||||
MSG_CRT_ERROR_SELF (LogLevel.ERROR, R.string.msg_crt_error_self),
|
||||
MSG_CRT_ERROR_MASTER_NOT_FOUND (LogLevel.ERROR, R.string.msg_crt_error_master_not_found),
|
||||
MSG_CRT_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_crt_error_nothing),
|
||||
|
@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.bcpg.S2K;
|
||||
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
@ -29,6 +30,7 @@ import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||
@ -44,6 +46,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
@ -137,7 +140,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
// It means the passphrase is empty
|
||||
return SecretKeyType.PASSPHRASE_EMPTY;
|
||||
} catch (PGPException e) {
|
||||
HashMap<String,String> notation = getRing().getLocalNotationData();
|
||||
HashMap<String, String> notation = getRing().getLocalNotationData();
|
||||
if (notation.containsKey("unlock.pin@sufficientlysecure.org")
|
||||
&& "1".equals(notation.get("unlock.pin@sufficientlysecure.org"))) {
|
||||
return SecretKeyType.PIN;
|
||||
@ -176,33 +179,13 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all supported hash algorithms. This list is currently hardcoded to return
|
||||
* a limited set of algorithms supported by Yubikeys.
|
||||
*
|
||||
* @return
|
||||
* Returns a list of all supported hash algorithms.
|
||||
*/
|
||||
public LinkedList<Integer> getSupportedHashAlgorithms() {
|
||||
LinkedList<Integer> supported = new LinkedList<>();
|
||||
public ArrayList<Integer> getSupportedHashAlgorithms() {
|
||||
// TODO: intersection between preferred hash algos of this key and PgpConstants.PREFERRED_HASH_ALGORITHMS
|
||||
// choose best algo
|
||||
|
||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
||||
// No support for MD5
|
||||
supported.add(HashAlgorithmTags.RIPEMD160);
|
||||
supported.add(HashAlgorithmTags.SHA1);
|
||||
supported.add(HashAlgorithmTags.SHA224);
|
||||
supported.add(HashAlgorithmTags.SHA256);
|
||||
supported.add(HashAlgorithmTags.SHA384);
|
||||
supported.add(HashAlgorithmTags.SHA512); // preferred is latest
|
||||
} else {
|
||||
supported.add(HashAlgorithmTags.MD5);
|
||||
supported.add(HashAlgorithmTags.RIPEMD160);
|
||||
supported.add(HashAlgorithmTags.SHA1);
|
||||
supported.add(HashAlgorithmTags.SHA224);
|
||||
supported.add(HashAlgorithmTags.SHA256);
|
||||
supported.add(HashAlgorithmTags.SHA384);
|
||||
supported.add(HashAlgorithmTags.SHA512); // preferred is latest
|
||||
}
|
||||
|
||||
return supported;
|
||||
return PgpConstants.sPreferredHashAlgorithms;
|
||||
}
|
||||
|
||||
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, byte[] nfcSignedHash,
|
||||
@ -286,7 +269,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
* Certify the given pubkeyid with the given masterkeyid.
|
||||
*
|
||||
* @param publicKeyRing Keyring to add certification to.
|
||||
* @param userIds User IDs to certify, or all if null
|
||||
* @param userIds User IDs to certify
|
||||
* @return A keyring with added certifications
|
||||
*/
|
||||
public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds,
|
||||
@ -331,10 +314,8 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
|
||||
|
||||
// fetch public key ring, add the certification and return it
|
||||
Iterable<String> it = userIds != null ? userIds
|
||||
: new IterableIterator<String>(publicKey.getUserIDs());
|
||||
try {
|
||||
for (String userId : it) {
|
||||
for (String userId : userIds) {
|
||||
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
|
||||
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
|
||||
}
|
||||
@ -348,6 +329,71 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
return new UncachedKeyRing(ring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Certify the given user attributes with the given masterkeyid.
|
||||
*
|
||||
* @param publicKeyRing Keyring to add certification to.
|
||||
* @param userAttributes User IDs to certify, or all if null
|
||||
* @return A keyring with added certifications
|
||||
*/
|
||||
public UncachedKeyRing certifyUserAttributes(CanonicalizedPublicKeyRing publicKeyRing,
|
||||
List<WrappedUserAttribute> userAttributes, byte[] nfcSignedHash, Date nfcCreationTimestamp) {
|
||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||
throw new PrivateKeyNotUnlockedException();
|
||||
}
|
||||
if (!isMasterKey()) {
|
||||
throw new AssertionError("tried to certify with non-master key, this is a programming error!");
|
||||
}
|
||||
if (publicKeyRing.getMasterKeyId() == getKeyId()) {
|
||||
throw new AssertionError("key tried to self-certify, this is a programming error!");
|
||||
}
|
||||
|
||||
// create a signatureGenerator from the supplied masterKeyId and passphrase
|
||||
PGPSignatureGenerator signatureGenerator;
|
||||
{
|
||||
// TODO: SHA256 fixed?
|
||||
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(PGPUtil.SHA256,
|
||||
nfcSignedHash, nfcCreationTimestamp);
|
||||
|
||||
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||
try {
|
||||
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
|
||||
} catch (PGPException e) {
|
||||
Log.e(Constants.TAG, "signing error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
{ // supply signatureGenerator with a SubpacketVector
|
||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||
if (nfcCreationTimestamp != null) {
|
||||
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
|
||||
Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp);
|
||||
}
|
||||
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
||||
signatureGenerator.setHashedSubpackets(packetVector);
|
||||
}
|
||||
|
||||
// get the master subkey (which we certify for)
|
||||
PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
|
||||
|
||||
// fetch public key ring, add the certification and return it
|
||||
try {
|
||||
for (WrappedUserAttribute userAttribute : userAttributes) {
|
||||
PGPUserAttributeSubpacketVector vector = userAttribute.getVector();
|
||||
PGPSignature sig = signatureGenerator.generateCertification(vector, publicKey);
|
||||
publicKey = PGPPublicKey.addCertification(publicKey, vector, sig);
|
||||
}
|
||||
} catch (PGPException e) {
|
||||
Log.e(Constants.TAG, "signing error", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
|
||||
|
||||
return new UncachedKeyRing(ring);
|
||||
}
|
||||
|
||||
static class PrivateKeyNotUnlockedException extends RuntimeException {
|
||||
// this exception is a programming error which happens when an operation which requires
|
||||
// the private key is called without a previous call to unlock()
|
||||
@ -358,7 +404,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
}
|
||||
|
||||
// HACK, for TESTING ONLY!!
|
||||
PGPPrivateKey getPrivateKey () {
|
||||
PGPPrivateKey getPrivateKey() {
|
||||
return mPrivateKey;
|
||||
}
|
||||
|
||||
|
@ -19,11 +19,11 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
@ -45,7 +45,7 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
|
||||
public CanonicalizedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
|
||||
{
|
||||
super(verified);
|
||||
PGPObjectFactory factory = new PGPObjectFactory(blob);
|
||||
JcaPGPObjectFactory factory = new JcaPGPObjectFactory(blob);
|
||||
PGPKeyRing keyRing = null;
|
||||
try {
|
||||
if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
|
||||
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PgpConstants {
|
||||
|
||||
public static ArrayList<Integer> sPreferredSymmetricAlgorithms = new ArrayList<>();
|
||||
public static ArrayList<Integer> sPreferredHashAlgorithms = new ArrayList<>();
|
||||
public static ArrayList<Integer> sPreferredCompressionAlgorithms = new ArrayList<>();
|
||||
|
||||
// TODO: use hashmaps for contains in O(1) and intersections!
|
||||
|
||||
/*
|
||||
* Most preferred is first
|
||||
* These arrays are written as preferred algorithms into the keys on creation.
|
||||
* Other implementations may choose to honor this selection.
|
||||
*
|
||||
* These lists also define the only algorithms which are used in OpenKeychain.
|
||||
* We do not support algorithms such as MD5
|
||||
*/
|
||||
static {
|
||||
sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_256);
|
||||
sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_192);
|
||||
sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_128);
|
||||
sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.TWOFISH);
|
||||
|
||||
// NOTE: some implementations do not support SHA512, thus we choose SHA256 as default (Mailvelope?)
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA256);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA512);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA384);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA224);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA1);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.RIPEMD160);
|
||||
|
||||
sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZLIB);
|
||||
sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.BZIP2);
|
||||
sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZIP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: s2kcount is a number between 0 and 0xff that controls the
|
||||
* number of times to iterate the password hash before use. More
|
||||
* iterations are useful against offline attacks, as it takes more
|
||||
* time to check each password. The actual number of iterations is
|
||||
* rather complex, and also depends on the hash function in use.
|
||||
* Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
|
||||
* you more iterations. As a rough rule of thumb, when using
|
||||
* SHA256 as the hashing function, 0x10 gives you about 64
|
||||
* iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
|
||||
* or about 1 million iterations. The maximum you can go to is
|
||||
* 0xff, or about 2 million iterations.
|
||||
* from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
|
||||
*
|
||||
* Bouncy Castle default: 0x60
|
||||
* kbsriram proposes: 0xc0
|
||||
* OpenKeychain: 0x90
|
||||
*/
|
||||
public static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90;
|
||||
public static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA256;
|
||||
public static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
|
||||
public static final int SECRET_KEY_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA256;
|
||||
// NOTE: only SHA1 is supported for key checksum calculations in OpenPGP,
|
||||
// see http://tools.ietf.org/html/rfc488 0#section-5.5.3
|
||||
public static final int SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO = HashAlgorithmTags.SHA1;
|
||||
|
||||
public static interface OpenKeychainSymmetricKeyAlgorithmTags extends SymmetricKeyAlgorithmTags {
|
||||
public static final int USE_PREFERRED = -1;
|
||||
}
|
||||
|
||||
public static interface OpenKeychainHashAlgorithmTags extends HashAlgorithmTags {
|
||||
public static final int USE_PREFERRED = -1;
|
||||
}
|
||||
|
||||
public static interface OpenKeychainCompressionAlgorithmTags extends CompressionAlgorithmTags {
|
||||
public static final int USE_PREFERRED = -1;
|
||||
}
|
||||
|
||||
public static int[] getAsArray(ArrayList<Integer> list) {
|
||||
int[] array = new int[list.size()];
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
array[i] = list.get(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
@ -563,6 +563,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
log.add(LogType.MSG_DC_PREP_STREAMS, indent);
|
||||
|
||||
// we made sure above one of these two would be true
|
||||
int symmetricEncryptionAlgo;
|
||||
if (symmetricPacketFound) {
|
||||
currentProgress += 2;
|
||||
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
||||
@ -576,6 +577,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
|
||||
encryptedData = encryptedDataSymmetric;
|
||||
|
||||
symmetricEncryptionAlgo = encryptedDataSymmetric.getSymmetricAlgorithm(decryptorFactory);
|
||||
} else if (asymmetricPacketFound) {
|
||||
currentProgress += 2;
|
||||
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
|
||||
@ -598,6 +600,8 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
PublicKeyDataDecryptorFactory decryptorFactory
|
||||
= secretEncryptionKey.getDecryptorFactory(mDecryptedSessionKey);
|
||||
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
|
||||
|
||||
symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
|
||||
} catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {
|
||||
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
|
||||
DecryptVerifyResult result =
|
||||
@ -614,6 +618,11 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
// Warn about old encryption algorithms!
|
||||
if (!PgpConstants.sPreferredSymmetricAlgorithms.contains(symmetricEncryptionAlgo)) {
|
||||
log.add(LogType.MSG_DC_OLD_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
|
||||
}
|
||||
|
||||
JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
|
||||
Object dataChunk = plainFact.nextObject();
|
||||
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
|
||||
@ -811,6 +820,13 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
} else {
|
||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
||||
}
|
||||
|
||||
// Don't allow verification of old hash algorithms!
|
||||
if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
|
||||
validSignature = false;
|
||||
log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
|
||||
}
|
||||
|
||||
signatureResultBuilder.setValidSignature(validSignature);
|
||||
}
|
||||
|
||||
@ -936,6 +952,13 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
} else {
|
||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
||||
}
|
||||
|
||||
// Don't allow verification of old hash algorithms!
|
||||
if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
|
||||
validSignature = false;
|
||||
log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
|
||||
}
|
||||
|
||||
signatureResultBuilder.setValidSignature(validSignature);
|
||||
|
||||
} catch (SignatureException e) {
|
||||
@ -1024,6 +1047,13 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
} else {
|
||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
||||
}
|
||||
|
||||
// Don't allow verification of old hash algorithms!
|
||||
if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
|
||||
validSignature = false;
|
||||
log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
|
||||
}
|
||||
|
||||
signatureResultBuilder.setValidSignature(validSignature);
|
||||
}
|
||||
|
||||
|
@ -47,26 +47,6 @@ public class PgpHelper {
|
||||
".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
|
||||
Pattern.DOTALL);
|
||||
|
||||
public static String getVersion(Context context) {
|
||||
String version;
|
||||
try {
|
||||
PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0);
|
||||
version = pi.versionName;
|
||||
return version;
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(Constants.TAG, "Version could not be retrieved!", e);
|
||||
return "0.0";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getVersionForHeader(Context context) {
|
||||
if(Preferences.getPreferences(context).getWriteVersionHeader()){
|
||||
return "OpenKeychain v" + getVersion(context);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes file securely by overwriting it with random data before deleting it.
|
||||
* <p/>
|
||||
|
@ -18,9 +18,7 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
import org.spongycastle.bcpg.sig.Features;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.spongycastle.jce.spec.ElGamalParameterSpec;
|
||||
@ -90,49 +88,6 @@ public class PgpKeyOperation {
|
||||
private Stack<Progressable> mProgress;
|
||||
private AtomicBoolean mCancelled;
|
||||
|
||||
// most preferred is first
|
||||
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
|
||||
SymmetricKeyAlgorithmTags.AES_256,
|
||||
SymmetricKeyAlgorithmTags.AES_192,
|
||||
SymmetricKeyAlgorithmTags.AES_128,
|
||||
SymmetricKeyAlgorithmTags.CAST5
|
||||
};
|
||||
private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{
|
||||
HashAlgorithmTags.SHA512,
|
||||
HashAlgorithmTags.SHA384,
|
||||
HashAlgorithmTags.SHA256,
|
||||
HashAlgorithmTags.SHA224,
|
||||
HashAlgorithmTags.RIPEMD160
|
||||
};
|
||||
private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{
|
||||
CompressionAlgorithmTags.ZLIB,
|
||||
CompressionAlgorithmTags.BZIP2,
|
||||
CompressionAlgorithmTags.ZIP
|
||||
};
|
||||
|
||||
/*
|
||||
* Note: s2kcount is a number between 0 and 0xff that controls the
|
||||
* number of times to iterate the password hash before use. More
|
||||
* iterations are useful against offline attacks, as it takes more
|
||||
* time to check each password. The actual number of iterations is
|
||||
* rather complex, and also depends on the hash function in use.
|
||||
* Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
|
||||
* you more iterations. As a rough rule of thumb, when using
|
||||
* SHA256 as the hashing function, 0x10 gives you about 64
|
||||
* iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
|
||||
* or about 1 million iterations. The maximum you can go to is
|
||||
* 0xff, or about 2 million iterations.
|
||||
* from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
|
||||
*
|
||||
* Bouncy Castle default: 0x60
|
||||
* kbsriram proposes 0xc0
|
||||
* we use 0x90, a good trade-off between usability and security against offline attacks
|
||||
*/
|
||||
private static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90;
|
||||
private static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA256;
|
||||
private static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
|
||||
private static final int SECRET_KEY_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA256;
|
||||
|
||||
public PgpKeyOperation(Progressable progress) {
|
||||
super();
|
||||
if (progress != null) {
|
||||
@ -346,14 +301,14 @@ public class PgpKeyOperation {
|
||||
|
||||
// Build key encrypter and decrypter based on passphrase
|
||||
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
.build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
|
||||
SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO,
|
||||
encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
|
||||
|
||||
// NOTE: only SHA1 is supported for key checksum calculations.
|
||||
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(HashAlgorithmTags.SHA1);
|
||||
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
|
||||
PGPSecretKey masterSecretKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
|
||||
sha1Calc, true, keyEncryptor);
|
||||
|
||||
@ -880,14 +835,14 @@ public class PgpKeyOperation {
|
||||
PGPSecretKey sKey; {
|
||||
// Build key encrypter and decrypter based on passphrase
|
||||
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
.build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
|
||||
SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
|
||||
// NOTE: only SHA1 is supported for key checksum calculations.
|
||||
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(HashAlgorithmTags.SHA1);
|
||||
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
|
||||
sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, sha1Calc, false, keyEncryptor);
|
||||
}
|
||||
|
||||
@ -1026,7 +981,8 @@ public class PgpKeyOperation {
|
||||
|
||||
// add packet with EMPTY notation data (updates old one, but will be stripped later)
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
{ // set subpackets
|
||||
@ -1052,7 +1008,8 @@ public class PgpKeyOperation {
|
||||
|
||||
// add packet with "pin" notation data
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
{ // set subpackets
|
||||
@ -1099,12 +1056,13 @@ public class PgpKeyOperation {
|
||||
OperationLog log, int indent) throws PGPException {
|
||||
|
||||
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
|
||||
.get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
.get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
// Build key encryptor based on new passphrase
|
||||
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
|
||||
SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||
newPassphrase.toCharArray());
|
||||
|
||||
@ -1237,7 +1195,8 @@ public class PgpKeyOperation {
|
||||
int flags, long expiry)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
|
||||
@ -1254,9 +1213,12 @@ public class PgpKeyOperation {
|
||||
* error than be ignored.
|
||||
*/
|
||||
/* non-critical subpackets: */
|
||||
hashedPacketsGen.setPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS);
|
||||
hashedPacketsGen.setPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS);
|
||||
hashedPacketsGen.setPreferredCompressionAlgorithms(false, PREFERRED_COMPRESSION_ALGORITHMS);
|
||||
hashedPacketsGen.setPreferredSymmetricAlgorithms(false,
|
||||
PgpConstants.getAsArray(PgpConstants.sPreferredSymmetricAlgorithms));
|
||||
hashedPacketsGen.setPreferredHashAlgorithms(false,
|
||||
PgpConstants.getAsArray(PgpConstants.sPreferredHashAlgorithms));
|
||||
hashedPacketsGen.setPreferredCompressionAlgorithms(false,
|
||||
PgpConstants.getAsArray(PgpConstants.sPreferredCompressionAlgorithms));
|
||||
hashedPacketsGen.setPrimaryUserID(false, primary);
|
||||
|
||||
/* critical subpackets: we consider those important for a modern pgp implementation */
|
||||
@ -1280,7 +1242,8 @@ public class PgpKeyOperation {
|
||||
PGPUserAttributeSubpacketVector vector)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
|
||||
@ -1299,7 +1262,8 @@ public class PgpKeyOperation {
|
||||
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
@ -1313,7 +1277,7 @@ public class PgpKeyOperation {
|
||||
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPublicKey.getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
@ -1357,7 +1321,7 @@ public class PgpKeyOperation {
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, creationTime);
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
pKey.getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
|
||||
@ -1378,7 +1342,7 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPublicKey.getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
|
||||
|
@ -12,10 +12,10 @@ public class PgpSignEncryptInput {
|
||||
protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
|
||||
protected long[] mEncryptionMasterKeyIds = null;
|
||||
protected String mSymmetricPassphrase = null;
|
||||
protected int mSymmetricEncryptionAlgorithm = 0;
|
||||
protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED;
|
||||
protected long mSignatureMasterKeyId = Constants.key.none;
|
||||
protected Long mSignatureSubKeyId = null;
|
||||
protected int mSignatureHashAlgorithm = 0;
|
||||
protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
|
||||
protected String mSignaturePassphrase = null;
|
||||
protected long mAdditionalEncryptId = Constants.key.none;
|
||||
protected byte[] mNfcSignedHash = null;
|
||||
|
@ -25,7 +25,6 @@ import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||
import org.spongycastle.bcpg.BCPGOutputStream;
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.openpgp.PGPCompressedDataGenerator;
|
||||
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||
import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPLiteralData;
|
||||
@ -58,6 +57,7 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
@ -206,12 +206,12 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
// check if hash algo is supported
|
||||
// Use preferred hash algo
|
||||
int requestedAlgorithm = input.getSignatureHashAlgorithm();
|
||||
LinkedList<Integer> supported = signingKey.getSupportedHashAlgorithms();
|
||||
if (requestedAlgorithm == 0) {
|
||||
ArrayList<Integer> supported = signingKey.getSupportedHashAlgorithms();
|
||||
if (requestedAlgorithm == PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED) {
|
||||
// get most preferred
|
||||
input.setSignatureHashAlgorithm(supported.getLast());
|
||||
input.setSignatureHashAlgorithm(supported.get(0));
|
||||
} else if (!supported.contains(requestedAlgorithm)) {
|
||||
log.add(LogType.MSG_PSE_ERROR_HASH_ALGO, indent);
|
||||
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
||||
@ -222,9 +222,13 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
/* Initialize PGPEncryptedDataGenerator for later usage */
|
||||
PGPEncryptedDataGenerator cPk = null;
|
||||
if (enableEncryption) {
|
||||
|
||||
// Use preferred encryption algo
|
||||
int algo = input.getSymmetricEncryptionAlgorithm();
|
||||
if (algo == 0) {
|
||||
algo = PGPEncryptedData.AES_128;
|
||||
if (algo == PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED) {
|
||||
// get most preferred
|
||||
// TODO: get from recipients
|
||||
algo = PgpConstants.sPreferredSymmetricAlgorithms.get(0);
|
||||
}
|
||||
// has Integrity packet enabled!
|
||||
JcePGPDataEncryptorBuilder encryptorBuilder =
|
||||
|
@ -657,7 +657,7 @@ public class KeychainProvider extends ContentProvider {
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
}
|
||||
|
||||
if (Constants.DEBUG) {
|
||||
if (Constants.DEBUG && Constants.DEBUG_LOG_DB_QUERIES) {
|
||||
Log.d(Constants.TAG,
|
||||
"Query: "
|
||||
+ qb.buildQuery(projection, selection, selectionArgs, null, null,
|
||||
|
@ -1369,8 +1369,7 @@ public class ProviderHelper {
|
||||
UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
String version = PgpHelper.getVersionForHeader(mContext);
|
||||
keyRing.encodeArmored(bos, version);
|
||||
keyRing.encodeArmored(bos, null);
|
||||
String armoredKey = bos.toString("UTF-8");
|
||||
|
||||
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
|
||||
|
@ -30,13 +30,16 @@ import org.openintents.openpgp.OpenPgpError;
|
||||
import org.openintents.openpgp.OpenPgpMetadata;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
|
||||
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpConstants;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
@ -52,6 +55,7 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
|
||||
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -258,8 +262,8 @@ public class OpenPgpService extends RemoteService {
|
||||
.setEnableAsciiArmorOutput(asciiArmor)
|
||||
.setCleartextSignature(cleartextSign)
|
||||
.setDetachedSignature(!cleartextSign)
|
||||
.setVersionHeader(PgpHelper.getVersionForHeader(this))
|
||||
.setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
|
||||
.setVersionHeader(null)
|
||||
.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
|
||||
.setSignatureMasterKeyId(accSettings.getKeyId())
|
||||
.setNfcState(nfcSignedHash, nfcCreationDate);
|
||||
|
||||
@ -356,9 +360,9 @@ public class OpenPgpService extends RemoteService {
|
||||
|
||||
PgpSignEncryptInput pseInput = new PgpSignEncryptInput();
|
||||
pseInput.setEnableAsciiArmorOutput(asciiArmor)
|
||||
.setVersionHeader(PgpHelper.getVersionForHeader(this))
|
||||
.setCompressionId(accSettings.getCompression())
|
||||
.setSymmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm())
|
||||
.setVersionHeader(null)
|
||||
.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED)
|
||||
.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED)
|
||||
.setEncryptionMasterKeyIds(keyIds)
|
||||
.setFailOnMissingEncryptionKeyIds(true)
|
||||
.setAdditionalEncryptId(accSettings.getKeyId()); // add acc key for encryption
|
||||
@ -374,7 +378,7 @@ public class OpenPgpService extends RemoteService {
|
||||
}
|
||||
|
||||
// sign and encrypt
|
||||
pseInput.setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
|
||||
pseInput.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
|
||||
.setSignatureMasterKeyId(accSettings.getKeyId())
|
||||
.setNfcState(nfcSignedHash, nfcCreationDate);
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ import android.os.Parcelable;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
|
||||
|
||||
|
||||
/**
|
||||
* This class is a a transferable representation for a number of keyrings to
|
||||
* be certified.
|
||||
@ -76,14 +79,19 @@ public class CertifyActionsParcel implements Parcelable {
|
||||
final public long mMasterKeyId;
|
||||
|
||||
final public ArrayList<String> mUserIds;
|
||||
|
||||
public CertifyAction(long masterKeyId) {
|
||||
this(masterKeyId, null);
|
||||
}
|
||||
final public ArrayList<WrappedUserAttribute> mUserAttributes;
|
||||
|
||||
public CertifyAction(long masterKeyId, ArrayList<String> userIds) {
|
||||
mMasterKeyId = masterKeyId;
|
||||
mUserIds = userIds;
|
||||
mUserAttributes = null;
|
||||
}
|
||||
|
||||
public CertifyAction(long masterKeyId, ArrayList<String> userIds,
|
||||
ArrayList<WrappedUserAttribute> attributes) {
|
||||
mMasterKeyId = masterKeyId;
|
||||
mUserIds = userIds;
|
||||
mUserAttributes = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,9 +20,6 @@ package org.sufficientlysecure.keychain.ui;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Patterns;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -33,17 +30,19 @@ import android.widget.EditText;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
||||
import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
|
||||
import org.sufficientlysecure.keychain.ui.widget.PasswordEditText;
|
||||
import org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator.PasswordStrengthView;
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class CreateKeyInputFragment extends Fragment {
|
||||
|
||||
CreateKeyActivity mCreateKeyActivity;
|
||||
|
||||
PasswordStrengthView mPassphraseStrengthView;
|
||||
AutoCompleteTextView mNameEdit;
|
||||
AutoCompleteTextView mEmailEdit;
|
||||
EditText mPassphraseEdit;
|
||||
EmailEditText mEmailEdit;
|
||||
PasswordEditText mPassphraseEdit;
|
||||
EditText mPassphraseEditAgain;
|
||||
View mCreateButton;
|
||||
|
||||
@ -69,9 +68,11 @@ public class CreateKeyInputFragment extends Fragment {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.create_key_input_fragment, container, false);
|
||||
|
||||
mPassphraseStrengthView = (PasswordStrengthView) view.findViewById(R.id
|
||||
.create_key_passphrase_strength);
|
||||
mNameEdit = (AutoCompleteTextView) view.findViewById(R.id.create_key_name);
|
||||
mEmailEdit = (AutoCompleteTextView) view.findViewById(R.id.create_key_email);
|
||||
mPassphraseEdit = (EditText) view.findViewById(R.id.create_key_passphrase);
|
||||
mEmailEdit = (EmailEditText) view.findViewById(R.id.create_key_email);
|
||||
mPassphraseEdit = (PasswordEditText) view.findViewById(R.id.create_key_passphrase);
|
||||
mPassphraseEditAgain = (EditText) view.findViewById(R.id.create_key_passphrase_again);
|
||||
mCreateButton = view.findViewById(R.id.create_key_button);
|
||||
|
||||
@ -95,33 +96,7 @@ public class CreateKeyInputFragment extends Fragment {
|
||||
ContactHelper.getPossibleUserEmails(getActivity())
|
||||
)
|
||||
);
|
||||
mEmailEdit.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
String email = editable.toString();
|
||||
if (email.length() > 0) {
|
||||
Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
|
||||
if (emailMatcher.matches()) {
|
||||
mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
|
||||
R.drawable.uid_mail_ok, 0);
|
||||
} else {
|
||||
mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
|
||||
R.drawable.uid_mail_bad, 0);
|
||||
}
|
||||
} else {
|
||||
// remove drawable if email is empty
|
||||
mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mNameEdit.setThreshold(1); // Start working from first character
|
||||
mNameEdit.setAdapter(
|
||||
@ -131,6 +106,15 @@ public class CreateKeyInputFragment extends Fragment {
|
||||
)
|
||||
);
|
||||
|
||||
// Edit text padding doesn't work via xml (http://code.google.com/p/android/issues/detail?id=77982)
|
||||
// so we set the right padding programmatically.
|
||||
mPassphraseEdit.setPadding(mPassphraseEdit.getPaddingLeft(),
|
||||
mPassphraseEdit.getPaddingTop(),
|
||||
(int) (56 * getResources().getDisplayMetrics().density),
|
||||
mPassphraseEdit.getPaddingBottom());
|
||||
|
||||
mPassphraseEdit.setPasswordStrengthView(mPassphraseStrengthView);
|
||||
|
||||
mCreateButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -243,7 +243,7 @@ public abstract class DecryptFragment extends Fragment {
|
||||
|
||||
private void setShowAction(final long signatureKeyId) {
|
||||
mSignatureAction.setText(R.string.decrypt_result_action_show);
|
||||
mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_accounts, 0);
|
||||
mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_vpn_key_grey_24dp, 0);
|
||||
mSignatureLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -28,6 +28,7 @@ public interface EncryptActivityInterface {
|
||||
}
|
||||
|
||||
public boolean isUseArmor();
|
||||
public boolean isUseCompression();
|
||||
|
||||
public long getSignatureKey();
|
||||
public long[] getEncryptionKeys();
|
||||
|
@ -25,11 +25,13 @@ import android.support.v4.app.Fragment;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
|
||||
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpConstants;
|
||||
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
@ -66,6 +68,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
private long mSigningKeyId = Constants.key.none;
|
||||
private String mPassphrase = "";
|
||||
private boolean mUseArmor;
|
||||
private boolean mUseCompression;
|
||||
private boolean mDeleteAfterEncrypt = false;
|
||||
private boolean mShareAfterEncrypt = false;
|
||||
private ArrayList<Uri> mInputUris;
|
||||
@ -81,6 +84,11 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
return mUseArmor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseCompression() {
|
||||
return mUseCompression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSignatureKey() {
|
||||
return mSigningKeyId;
|
||||
@ -196,10 +204,13 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
data.addInputUris(mInputUris);
|
||||
data.addOutputUris(mOutputUris);
|
||||
|
||||
data.setCompressionId(Preferences.getPreferences(this).getDefaultMessageCompression());
|
||||
|
||||
// Always use armor for messages
|
||||
data.setEnableAsciiArmorOutput(mUseArmor);
|
||||
if (mUseCompression) {
|
||||
data.setCompressionId(PgpConstants.sPreferredCompressionAlgorithms.get(0));
|
||||
} else {
|
||||
data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
|
||||
}
|
||||
data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
|
||||
data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
|
||||
|
||||
if (isModeSymmetric()) {
|
||||
Log.d(Constants.TAG, "Symmetric encryption enabled!");
|
||||
@ -315,8 +326,6 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
// Handle intent actions
|
||||
handleActions(getIntent());
|
||||
updateModeFragment();
|
||||
|
||||
mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -327,7 +336,6 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.encrypt_file_activity, menu);
|
||||
menu.findItem(R.id.check_use_armor).setChecked(mUseArmor);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@ -348,21 +356,30 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
item.setChecked(!item.isChecked());
|
||||
}
|
||||
switch (item.getItemId()) {
|
||||
case R.id.check_use_symmetric:
|
||||
case R.id.check_use_symmetric: {
|
||||
mCurrentMode = item.isChecked() ? MODE_SYMMETRIC : MODE_ASYMMETRIC;
|
||||
updateModeFragment();
|
||||
notifyUpdate();
|
||||
break;
|
||||
case R.id.check_use_armor:
|
||||
}
|
||||
case R.id.check_use_armor: {
|
||||
mUseArmor = item.isChecked();
|
||||
notifyUpdate();
|
||||
break;
|
||||
case R.id.check_delete_after_encrypt:
|
||||
}
|
||||
case R.id.check_delete_after_encrypt: {
|
||||
mDeleteAfterEncrypt = item.isChecked();
|
||||
notifyUpdate();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
case R.id.check_enable_compression: {
|
||||
mUseCompression = item.isChecked();
|
||||
notifyUpdate();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -25,12 +25,14 @@ import android.support.v4.app.Fragment;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
|
||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpConstants;
|
||||
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
@ -70,6 +72,7 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
private ArrayList<Uri> mInputUris;
|
||||
private ArrayList<Uri> mOutputUris;
|
||||
private String mMessage = "";
|
||||
private boolean mUseCompression;
|
||||
|
||||
public boolean isModeSymmetric() {
|
||||
return MODE_SYMMETRIC == mCurrentMode;
|
||||
@ -80,6 +83,11 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseCompression() {
|
||||
return mUseCompression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSignatureKey() {
|
||||
return mSigningKeyId;
|
||||
@ -189,7 +197,13 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
data.setBytes(mMessage.getBytes());
|
||||
data.setCleartextSignature(true);
|
||||
|
||||
data.setCompressionId(Preferences.getPreferences(this).getDefaultMessageCompression());
|
||||
if (mUseCompression) {
|
||||
data.setCompressionId(PgpConstants.sPreferredCompressionAlgorithms.get(0));
|
||||
} else {
|
||||
data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
|
||||
}
|
||||
data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
|
||||
data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
|
||||
|
||||
// Always use armor for messages
|
||||
data.setEnableAsciiArmorOutput(true);
|
||||
@ -328,13 +342,20 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
item.setChecked(!item.isChecked());
|
||||
}
|
||||
switch (item.getItemId()) {
|
||||
case R.id.check_use_symmetric:
|
||||
case R.id.check_use_symmetric: {
|
||||
mCurrentMode = item.isChecked() ? MODE_SYMMETRIC : MODE_ASYMMETRIC;
|
||||
updateModeFragment();
|
||||
notifyUpdate();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
case R.id.check_enable_compression: {
|
||||
mUseCompression = item.isChecked();
|
||||
notifyUpdate();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -678,9 +678,6 @@ public class KeyListFragment extends LoaderFragment
|
||||
|
||||
/**
|
||||
* Bind cursor data to the item list view
|
||||
* <p/>
|
||||
* NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method.
|
||||
* Thus no ViewHolder is required here.
|
||||
*/
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
|
@ -33,8 +33,6 @@ import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
|
||||
@ -93,12 +91,6 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
initializePassphraseCacheTtl(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
|
||||
|
||||
initializeEncryptionAlgorithm(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM));
|
||||
|
||||
initializeHashAlgorithm(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_HASH_ALGORITHM));
|
||||
|
||||
int[] valueIds = new int[]{
|
||||
CompressionAlgorithmTags.UNCOMPRESSED,
|
||||
CompressionAlgorithmTags.ZIP,
|
||||
@ -115,20 +107,6 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
values[i] = "" + valueIds[i];
|
||||
}
|
||||
|
||||
initializeMessageCompression(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION),
|
||||
entries, values);
|
||||
|
||||
initializeFileCompression(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION),
|
||||
entries, values);
|
||||
|
||||
initializeAsciiArmor(
|
||||
(CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR));
|
||||
|
||||
initializeWriteVersionHeader(
|
||||
(CheckBoxPreference) findPreference(Constants.Pref.WRITE_VERSION_HEADER));
|
||||
|
||||
initializeUseDefaultYubikeyPin(
|
||||
(CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN));
|
||||
|
||||
@ -265,12 +243,6 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
initializePassphraseCacheTtl(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
|
||||
|
||||
initializeEncryptionAlgorithm(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM));
|
||||
|
||||
initializeHashAlgorithm(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_HASH_ALGORITHM));
|
||||
|
||||
int[] valueIds = new int[]{
|
||||
CompressionAlgorithmTags.UNCOMPRESSED,
|
||||
CompressionAlgorithmTags.ZIP,
|
||||
@ -290,20 +262,6 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
values[i] = "" + valueIds[i];
|
||||
}
|
||||
|
||||
initializeMessageCompression(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION),
|
||||
entries, values);
|
||||
|
||||
initializeFileCompression(
|
||||
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION),
|
||||
entries, values);
|
||||
|
||||
initializeAsciiArmor(
|
||||
(CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR));
|
||||
|
||||
initializeWriteVersionHeader(
|
||||
(CheckBoxPreference) findPreference(Constants.Pref.WRITE_VERSION_HEADER));
|
||||
|
||||
initializeUseDefaultYubikeyPin(
|
||||
(CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN));
|
||||
|
||||
@ -344,113 +302,6 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private static void initializeEncryptionAlgorithm(final IntegerListPreference mEncryptionAlgorithm) {
|
||||
int valueIds[] = {PGPEncryptedData.AES_128, PGPEncryptedData.AES_192,
|
||||
PGPEncryptedData.AES_256, PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH,
|
||||
PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES,
|
||||
PGPEncryptedData.IDEA,};
|
||||
String entries[] = {"AES-128", "AES-192", "AES-256", "Blowfish", "Twofish", "CAST5",
|
||||
"DES", "Triple DES", "IDEA",};
|
||||
String values[] = new String[valueIds.length];
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
values[i] = "" + valueIds[i];
|
||||
}
|
||||
mEncryptionAlgorithm.setEntries(entries);
|
||||
mEncryptionAlgorithm.setEntryValues(values);
|
||||
mEncryptionAlgorithm.setValue("" + sPreferences.getDefaultEncryptionAlgorithm());
|
||||
mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
|
||||
mEncryptionAlgorithm
|
||||
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
mEncryptionAlgorithm.setValue(newValue.toString());
|
||||
mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
|
||||
sPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue
|
||||
.toString()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void initializeHashAlgorithm(final IntegerListPreference mHashAlgorithm) {
|
||||
int[] valueIds = new int[]{HashAlgorithmTags.RIPEMD160,
|
||||
HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256,
|
||||
HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512,};
|
||||
String[] entries = new String[]{"RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384",
|
||||
"SHA-512",};
|
||||
String[] values = new String[valueIds.length];
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
values[i] = "" + valueIds[i];
|
||||
}
|
||||
mHashAlgorithm.setEntries(entries);
|
||||
mHashAlgorithm.setEntryValues(values);
|
||||
mHashAlgorithm.setValue("" + sPreferences.getDefaultHashAlgorithm());
|
||||
mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
|
||||
mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
mHashAlgorithm.setValue(newValue.toString());
|
||||
mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
|
||||
sPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void initializeMessageCompression(final IntegerListPreference mMessageCompression,
|
||||
String[] entries, String[] values) {
|
||||
mMessageCompression.setEntries(entries);
|
||||
mMessageCompression.setEntryValues(values);
|
||||
mMessageCompression.setValue("" + sPreferences.getDefaultMessageCompression());
|
||||
mMessageCompression.setSummary(mMessageCompression.getEntry());
|
||||
mMessageCompression
|
||||
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
mMessageCompression.setValue(newValue.toString());
|
||||
mMessageCompression.setSummary(mMessageCompression.getEntry());
|
||||
sPreferences.setDefaultMessageCompression(Integer.parseInt(newValue
|
||||
.toString()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void initializeFileCompression
|
||||
(final IntegerListPreference mFileCompression, String[] entries, String[] values) {
|
||||
mFileCompression.setEntries(entries);
|
||||
mFileCompression.setEntryValues(values);
|
||||
mFileCompression.setValue("" + sPreferences.getDefaultFileCompression());
|
||||
mFileCompression.setSummary(mFileCompression.getEntry());
|
||||
mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
mFileCompression.setValue(newValue.toString());
|
||||
mFileCompression.setSummary(mFileCompression.getEntry());
|
||||
sPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void initializeAsciiArmor(final CheckBoxPreference mAsciiArmor) {
|
||||
mAsciiArmor.setChecked(sPreferences.getDefaultAsciiArmor());
|
||||
mAsciiArmor.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
mAsciiArmor.setChecked((Boolean) newValue);
|
||||
sPreferences.setDefaultAsciiArmor((Boolean) newValue);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void initializeWriteVersionHeader(final CheckBoxPreference mWriteVersionHeader) {
|
||||
mWriteVersionHeader.setChecked(sPreferences.getWriteVersionHeader());
|
||||
mWriteVersionHeader.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
mWriteVersionHeader.setChecked((Boolean) newValue);
|
||||
sPreferences.setWriteVersionHeader((Boolean) newValue);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void initializeSearchKeyserver(final CheckBoxPreference mSearchKeyserver) {
|
||||
Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs();
|
||||
mSearchKeyserver.setChecked(prefs.searchKeyserver);
|
||||
|
@ -90,6 +90,8 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
static final int REQUEST_QR_FINGERPRINT = 1;
|
||||
static final int REQUEST_DELETE= 2;
|
||||
static final int REQUEST_EXPORT= 3;
|
||||
|
||||
ExportHelper mExportHelper;
|
||||
ProviderHelper mProviderHelper;
|
||||
@ -278,54 +280,69 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
try {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home: {
|
||||
Intent homeIntent = new Intent(this, MainActivity.class);
|
||||
homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(homeIntent);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_export_file: {
|
||||
exportToFile(mDataUri, mExportHelper, mProviderHelper);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_delete: {
|
||||
deleteKey(mDataUri, mExportHelper);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_advanced: {
|
||||
Intent advancedIntent = new Intent(this, ViewKeyAdvActivity.class);
|
||||
advancedIntent.setData(mDataUri);
|
||||
startActivity(advancedIntent);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_refresh: {
|
||||
try {
|
||||
updateFromKeyserver(mDataUri, mProviderHelper);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_edit: {
|
||||
editKey(mDataUri);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_certify_fingerprint: {
|
||||
certifyFingeprint(mDataUri);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_add_linked_identity: {
|
||||
Intent intent = new Intent(this, LinkedIdWizard.class);
|
||||
intent.setData(mDataUri);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home: {
|
||||
Intent homeIntent = new Intent(this, MainActivity.class);
|
||||
homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(homeIntent);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_export_file: {
|
||||
Intent mIntent = new Intent(this,PassphraseDialogActivity.class);
|
||||
long keyId=0;
|
||||
try {
|
||||
keyId = new ProviderHelper(this)
|
||||
.getCachedPublicKeyRing(mDataUri)
|
||||
.extractOrGetMasterKeyId();
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId);
|
||||
startActivityForResult(mIntent,REQUEST_EXPORT);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_delete: {
|
||||
Intent mIntent = new Intent(this,PassphraseDialogActivity.class);
|
||||
long keyId=0;
|
||||
try {
|
||||
keyId = new ProviderHelper(this)
|
||||
.getCachedPublicKeyRing(mDataUri)
|
||||
.extractOrGetMasterKeyId();
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId);
|
||||
startActivityForResult(mIntent,REQUEST_DELETE);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_advanced: {
|
||||
Intent advancedIntent = new Intent(this, ViewKeyAdvActivity.class);
|
||||
advancedIntent.setData(mDataUri);
|
||||
startActivity(advancedIntent);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_refresh: {
|
||||
try {
|
||||
updateFromKeyserver(mDataUri, mProviderHelper);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_add_linked_identity: {
|
||||
Intent intent = new Intent(this, LinkedIdWizard.class);
|
||||
intent.setData(mDataUri);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_edit: {
|
||||
editKey(mDataUri);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_certify_fingerprint: {
|
||||
certifyFingeprint(mDataUri);
|
||||
return true;
|
||||
}
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
|
||||
Log.e(Constants.TAG, "Key not found", e);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
@ -417,6 +434,12 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
//Note:-Done due to the same weird crashes as for commitAllowingStateLoss()
|
||||
//super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
private void showQrCodeDialog() {
|
||||
Intent qrCodeIntent = new Intent(this, QrCodeViewActivity.class);
|
||||
|
||||
@ -490,6 +513,19 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK){
|
||||
deleteKey(mDataUri, mExportHelper);
|
||||
}
|
||||
if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK){
|
||||
try {
|
||||
exportToFile(mDataUri, mExportHelper, mProviderHelper);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
|
||||
Log.e(Constants.TAG, "Key not found", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
|
||||
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
|
||||
result.createNotify(this).show();
|
||||
|
@ -28,9 +28,6 @@ import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Patterns;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@ -46,11 +43,10 @@ import android.widget.TextView.OnEditorActionListener;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class AddUserIdDialogFragment extends DialogFragment implements OnEditorActionListener {
|
||||
private static final String ARG_MESSENGER = "messenger";
|
||||
private static final String ARG_NAME = "name";
|
||||
@ -62,7 +58,7 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA
|
||||
|
||||
private Messenger mMessenger;
|
||||
private AutoCompleteTextView mName;
|
||||
private AutoCompleteTextView mEmail;
|
||||
private EmailEditText mEmail;
|
||||
private EditText mComment;
|
||||
|
||||
public static AddUserIdDialogFragment newInstance(Messenger messenger, String predefinedName) {
|
||||
@ -99,38 +95,12 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA
|
||||
alert.setView(view);
|
||||
|
||||
mName = (AutoCompleteTextView) view.findViewById(R.id.add_user_id_name);
|
||||
mEmail = (AutoCompleteTextView) view.findViewById(R.id.add_user_id_address);
|
||||
mEmail = (EmailEditText) view.findViewById(R.id.add_user_id_address);
|
||||
mComment = (EditText) view.findViewById(R.id.add_user_id_comment);
|
||||
|
||||
mName.setText(predefinedName);
|
||||
|
||||
mEmail.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 afterTextChanged(Editable editable) {
|
||||
String email = editable.toString();
|
||||
if (email.length() > 0) {
|
||||
Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
|
||||
if (emailMatcher.matches()) {
|
||||
mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0,
|
||||
R.drawable.uid_mail_ok, 0);
|
||||
} else {
|
||||
mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0,
|
||||
R.drawable.uid_mail_bad, 0);
|
||||
}
|
||||
} else {
|
||||
// remove drawable if email is empty
|
||||
mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
mEmail.setThreshold(1); // Start working from first character
|
||||
mEmail.setAdapter(autoCompleteEmailAdapter);
|
||||
|
||||
|
@ -43,6 +43,8 @@ import android.widget.Toast;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.widget.PasswordEditText;
|
||||
import org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator.PasswordStrengthView;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener {
|
||||
@ -55,9 +57,10 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
public static final String MESSAGE_NEW_PASSPHRASE = "new_passphrase";
|
||||
|
||||
private Messenger mMessenger;
|
||||
private EditText mPassphraseEditText;
|
||||
private PasswordEditText mPassphraseEditText;
|
||||
private EditText mPassphraseAgainEditText;
|
||||
private CheckBox mNoPassphraseCheckBox;
|
||||
private PasswordStrengthView mPassphraseStrengthView;
|
||||
|
||||
/**
|
||||
* Creates new instance of this dialog fragment
|
||||
@ -97,9 +100,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
View view = inflater.inflate(R.layout.passphrase_repeat_dialog, null);
|
||||
alert.setView(view);
|
||||
|
||||
mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
|
||||
mPassphraseEditText = (PasswordEditText) view.findViewById(R.id.passphrase_passphrase);
|
||||
mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again);
|
||||
mNoPassphraseCheckBox = (CheckBox) view.findViewById(R.id.passphrase_no_passphrase);
|
||||
mPassphraseStrengthView = (PasswordStrengthView) view.findViewById(R.id.passphrase_repeat_passphrase_strength);
|
||||
mPassphraseEditText.setPasswordStrengthView(mPassphraseStrengthView);
|
||||
|
||||
|
||||
if (TextUtils.isEmpty(oldPassphrase)) {
|
||||
mNoPassphraseCheckBox.setChecked(true);
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.widget;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Patterns;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class EmailEditText extends AutoCompleteTextView {
|
||||
EmailEditText emailEditText;
|
||||
|
||||
public EmailEditText(Context context) {
|
||||
super(context);
|
||||
emailEditText = this;
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
this.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
public EmailEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
emailEditText = this;
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
this.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
public EmailEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
emailEditText = this;
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
this.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public EmailEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
emailEditText = this;
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
this.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
TextWatcher textWatcher = 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 afterTextChanged(Editable editable) {
|
||||
String email = editable.toString();
|
||||
if (email.length() > 0) {
|
||||
Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
|
||||
if (emailMatcher.matches()) {
|
||||
emailEditText.setCompoundDrawablesWithIntrinsicBounds(0, 0,
|
||||
R.drawable.uid_mail_ok, 0);
|
||||
} else {
|
||||
emailEditText.setCompoundDrawablesWithIntrinsicBounds(0, 0,
|
||||
R.drawable.uid_mail_bad, 0);
|
||||
}
|
||||
} else {
|
||||
// remove drawable if email is empty
|
||||
emailEditText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.widget;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.EditText;
|
||||
|
||||
import org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator.PasswordStrengthView;
|
||||
|
||||
/**
|
||||
* Developer: chipset
|
||||
* Package : org.sufficientlysecure.keychain.layouts
|
||||
* Project : open-keychain
|
||||
* Date : 6/3/15
|
||||
*/
|
||||
public class PasswordEditText extends EditText {
|
||||
|
||||
PasswordEditText passwordEditText;
|
||||
PasswordStrengthView passwordStrengthView;
|
||||
|
||||
public PasswordEditText(Context context) {
|
||||
super(context);
|
||||
passwordEditText = this;
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
this.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
public PasswordEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
passwordEditText = this;
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
this.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
public PasswordEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
passwordEditText = this;
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
this.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public PasswordEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
passwordEditText = this;
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
this.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
|
||||
TextWatcher textWatcher = 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 afterTextChanged(Editable editable) {
|
||||
String passphrase = editable.toString();
|
||||
passwordStrengthView.setPassword(passphrase);
|
||||
}
|
||||
};
|
||||
|
||||
// public PasswordStrengthView getPasswordStrengthView() {
|
||||
// return passwordStrengthView;
|
||||
// }
|
||||
|
||||
public void setPasswordStrengthView(PasswordStrengthView mPasswordStrengthView) {
|
||||
this.passwordStrengthView = mPasswordStrengthView;
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Matt Allen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
/**
|
||||
* Created by matt on 04/07/2014.
|
||||
* https://github.com/matt-allen/android-password-strength-indicator
|
||||
*/
|
||||
public class PasswordStrengthBarView extends PasswordStrengthView {
|
||||
|
||||
public PasswordStrengthBarView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mMinHeight = 80;
|
||||
mMinWidth = 300;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
generateIndicatorColor();
|
||||
// Default to full width
|
||||
int indWidth = mIndicatorWidth;
|
||||
// If score, leave it as full - can cause it to become
|
||||
// less than full width in this calculation
|
||||
if (mCurrentScore < 20) indWidth = (mIndicatorWidth / 20) * mCurrentScore;
|
||||
// Draw indicator
|
||||
canvas.drawRect(
|
||||
getPaddingLeft(),
|
||||
getPaddingTop(),
|
||||
indWidth,
|
||||
mIndicatorHeight,
|
||||
mIndicatorPaint
|
||||
);
|
||||
// Draw guides if true
|
||||
if (mShowGuides) {
|
||||
// TODO: Try and do this with a loop, for efficiency
|
||||
// Draw bottom guide border
|
||||
float positionY = getHeight() - getPaddingBottom() - getPaddingTop();
|
||||
float notchHeight = (float) (positionY * 0.8);
|
||||
canvas.drawLine(
|
||||
getPaddingLeft(),
|
||||
positionY,
|
||||
getWidth() - getPaddingRight(),
|
||||
positionY,
|
||||
mGuidePaint);
|
||||
// Show left-most notch
|
||||
canvas.drawLine(
|
||||
getPaddingLeft(),
|
||||
positionY,
|
||||
getPaddingLeft(),
|
||||
notchHeight,
|
||||
mGuidePaint
|
||||
);
|
||||
// Show middle-left notch
|
||||
canvas.drawLine(
|
||||
(float) (mIndicatorWidth * 0.25) + getPaddingLeft(),
|
||||
positionY,
|
||||
(float) (mIndicatorWidth * 0.25) + getPaddingLeft(),
|
||||
notchHeight,
|
||||
mGuidePaint
|
||||
);
|
||||
// Show the middle notch
|
||||
canvas.drawLine(
|
||||
(float) (mIndicatorWidth * 0.5) + getPaddingLeft(),
|
||||
positionY,
|
||||
(float) (mIndicatorWidth * 0.5) + getPaddingLeft(),
|
||||
notchHeight,
|
||||
mGuidePaint
|
||||
);
|
||||
// Show the middle-right notch
|
||||
canvas.drawLine(
|
||||
(float) (mIndicatorWidth * 0.75) + getPaddingLeft(),
|
||||
positionY,
|
||||
(float) (mIndicatorWidth * 0.75) + getPaddingLeft(),
|
||||
notchHeight,
|
||||
mGuidePaint
|
||||
);
|
||||
// Show the right-most notch
|
||||
canvas.drawLine(
|
||||
mIndicatorWidth + getPaddingLeft(),
|
||||
positionY,
|
||||
mIndicatorWidth + getPaddingLeft(),
|
||||
notchHeight,
|
||||
mGuidePaint
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,360 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Matt Allen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
|
||||
/**
|
||||
* Created by Matt Allen
|
||||
* 01/07/14
|
||||
* http://www.mattallensoftware.co.uk
|
||||
* mattallen092@gmail.com
|
||||
* <p/>
|
||||
* https://github.com/matt-allen/android-password-strength-indicator
|
||||
* <p/>
|
||||
* <p>
|
||||
* This View is designed to indicate how secure a user-entered password is in a visual way to
|
||||
* relay to the user if they need to make it stronger. The strength of the password can be set
|
||||
* at creation (or after) which will decide whether their password is strong enough.
|
||||
* </p>
|
||||
* <p/>
|
||||
* <p>
|
||||
* The password strength is decided by an index of 20. The minimum score needed to pass is 10
|
||||
* which means the String has met the conditions imposed by the strength test, but can be improved.
|
||||
* If the password scores 10-19 it is considered weak, and only if it scores 20 will it be
|
||||
* considered strong.
|
||||
* </p>
|
||||
*/
|
||||
public class PasswordStrengthView extends View {
|
||||
|
||||
protected static final int COLOR_FAIL = Color.parseColor("#e74c3c");
|
||||
protected static final int COLOR_WEAK = Color.parseColor("#e67e22");
|
||||
protected static final int COLOR_STRONG = Color.parseColor("#2ecc71");
|
||||
|
||||
protected int mMinWidth;
|
||||
protected int mMinHeight;
|
||||
|
||||
protected Paint mIndicatorPaint;
|
||||
protected Paint mGuidePaint;
|
||||
|
||||
protected int mIndicatorHeight;
|
||||
protected int mIndicatorWidth;
|
||||
protected int mCurrentScore;
|
||||
|
||||
protected int mColorFail;
|
||||
protected int mColorWeak;
|
||||
protected int mColorStrong;
|
||||
|
||||
protected boolean mShowGuides = true;
|
||||
|
||||
/**
|
||||
* Used to define that the indicator should only be looking
|
||||
* for a weak password. The bare minimum is used here to let
|
||||
* the user continue.
|
||||
*/
|
||||
public static final int STRENGTH_WEAK = 0;
|
||||
|
||||
/**
|
||||
* A fairly strict rule for generating a password. It encourages a password that is
|
||||
* less easy to crack.
|
||||
*/
|
||||
public static final int STRENGTH_MEDIUM = 1;
|
||||
|
||||
/**
|
||||
* A strong algorithm that encourages very strong passwords that should be fairly long, with
|
||||
* non-alphanumeric, numbers, and upper case.
|
||||
*/
|
||||
public static final int STRENGTH_STRONG = 2;
|
||||
|
||||
private int mStrengthRequirement = -1;
|
||||
protected String mPassword;
|
||||
|
||||
public PasswordStrengthView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
TypedArray style = context.getTheme().obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.PasswordStrengthView,
|
||||
0, 0);
|
||||
|
||||
try {
|
||||
mStrengthRequirement = style.getInteger(R.styleable.PasswordStrengthView_strength,
|
||||
STRENGTH_MEDIUM);
|
||||
mShowGuides = style.getBoolean(R.styleable.PasswordStrengthView_showGuides, true);
|
||||
mColorFail = style.getColor(R.styleable.PasswordStrengthView_color_fail, COLOR_FAIL);
|
||||
mColorWeak = style.getColor(R.styleable.PasswordStrengthView_color_weak, COLOR_WEAK);
|
||||
mColorStrong = style.getColor(R.styleable.PasswordStrengthView_color_strong,
|
||||
COLOR_STRONG);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// Create and style the paint used for drawing the guide on the indicator
|
||||
mGuidePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mGuidePaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
mGuidePaint.setColor(Color.BLACK);
|
||||
// Create and style paint for indicator
|
||||
mIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mIndicatorPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
/**
|
||||
* This view can determine if the password entered by the user is acceptable for
|
||||
* use by your use case. This is based on the strength requirement you have set.
|
||||
*
|
||||
* @return True if requirement has been met
|
||||
*/
|
||||
public boolean isStrengthRequirementMet() {
|
||||
return (mCurrentScore >= 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the strength requirement of the password entered by the user. This will also
|
||||
* re-check the password already entered against these new requirements.
|
||||
*
|
||||
* @param requiredStrength Use the public constants of this class to set
|
||||
*/
|
||||
public void setStrengthRequirement(int requiredStrength) {
|
||||
if (requiredStrength >= 0 && requiredStrength <= 2) {
|
||||
mStrengthRequirement = requiredStrength;
|
||||
if (mPassword != null && mPassword.length() > 0) {
|
||||
generatePasswordScore();
|
||||
// Update view with new score
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
} else {
|
||||
throw new IndexOutOfBoundsException("Input out of expected range");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the password string to check strength of
|
||||
*
|
||||
* @param passwordString String representation of user-input
|
||||
*/
|
||||
public void setPassword(String passwordString) {
|
||||
if (passwordString != null && passwordString.length() > 0) {
|
||||
mPassword = passwordString;
|
||||
generatePasswordScore();
|
||||
} else {
|
||||
mPassword = "";
|
||||
mCurrentScore = 0;
|
||||
}
|
||||
|
||||
// Update view with new score
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Private convenience method for adding to the password score
|
||||
*
|
||||
* @param score Amount to be added to current score
|
||||
*/
|
||||
protected void addToPasswordScore(int score) {
|
||||
int newScore = mCurrentScore + score;
|
||||
|
||||
// Limit max score
|
||||
if (newScore > 20) {
|
||||
mCurrentScore = 20;
|
||||
} else {
|
||||
mCurrentScore = newScore;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to determine the current strength requirement set on the algorithm
|
||||
*
|
||||
* @return Int representation of the current strength set for the indicator
|
||||
*/
|
||||
public int getStrengthRequirement() {
|
||||
return mStrengthRequirement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a score based on the password. The password will already need to be stored
|
||||
* as a class member before running this.
|
||||
*/
|
||||
protected void generatePasswordScore() {
|
||||
mCurrentScore = 0;
|
||||
int upperCase = getUppercaseCount(mPassword);
|
||||
int nonAlpha = getNonAlphanumericCount(mPassword);
|
||||
int numbers = getNumberCount(mPassword);
|
||||
switch (mStrengthRequirement) {
|
||||
case STRENGTH_WEAK:
|
||||
addToPasswordScore(mPassword.length() * 2);
|
||||
addToPasswordScore(upperCase * 2);
|
||||
addToPasswordScore(nonAlpha * 2);
|
||||
addToPasswordScore(numbers * 2);
|
||||
break;
|
||||
|
||||
case STRENGTH_MEDIUM:
|
||||
addToPasswordScore(mPassword.length());
|
||||
addToPasswordScore(upperCase);
|
||||
addToPasswordScore(nonAlpha * 2);
|
||||
addToPasswordScore(numbers);
|
||||
break;
|
||||
|
||||
case STRENGTH_STRONG:
|
||||
addToPasswordScore(mPassword.length() / 2);
|
||||
// Cut the score in half to make this a very high requirement
|
||||
addToPasswordScore(upperCase);
|
||||
addToPasswordScore(nonAlpha);
|
||||
addToPasswordScore(numbers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
|
||||
super.onSizeChanged(w, h, oldW, oldH);
|
||||
int paddingX = getPaddingLeft();
|
||||
int paddingY = getPaddingTop();
|
||||
mIndicatorHeight = h - paddingY;
|
||||
mIndicatorWidth = w - paddingX;
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard parts of the onMeasure needed to create the password strength
|
||||
* indicator. Subclasses should call super.onMeasure, but also need to set
|
||||
* the minimum height and width in the constructor.
|
||||
*
|
||||
* @param widthMeasureSpec The measurement given by the system
|
||||
* @param heightMeasureSpec The measurement given by the system
|
||||
*/
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// Set minimum space for the view to do it's thing
|
||||
int minW = getPaddingLeft() + getPaddingRight() + mMinWidth;
|
||||
int w = resolveSizeAndState(minW, widthMeasureSpec, 1);
|
||||
// And give it enough height so it's visible
|
||||
int minH = mMinHeight + getPaddingBottom() + getPaddingTop();
|
||||
int h = resolveSizeAndState(minH, heightMeasureSpec, 0);
|
||||
// Feed these back into UIKit
|
||||
setMeasuredDimension(w, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colour of the indicator {@code Paint} to one that is appropriate
|
||||
* for the strength of the password.
|
||||
*/
|
||||
protected void generateIndicatorColor() {
|
||||
int color = mColorFail;
|
||||
if (mCurrentScore >= 18) {
|
||||
color = mColorStrong;
|
||||
} else if (mCurrentScore >= 10) {
|
||||
color = mColorWeak;
|
||||
}
|
||||
mIndicatorPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick method to determine how many of the characters in a given string are upper case
|
||||
*
|
||||
* @param stringToCheck The string to examine
|
||||
* @return Number of upper case characters
|
||||
*/
|
||||
protected int getUppercaseCount(String stringToCheck) {
|
||||
int score = 0;
|
||||
int loops = stringToCheck.length() - 1;
|
||||
for (int i = 0; i <= loops; i++) {
|
||||
if (Character.isUpperCase(stringToCheck.charAt(i))) {
|
||||
score++;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to determine how many characters in the given String aren't
|
||||
* letters or numbers.
|
||||
*
|
||||
* @param stringToCheck
|
||||
* @return Number of characters that aren't numbers or letters
|
||||
*/
|
||||
protected int getNonAlphanumericCount(String stringToCheck) {
|
||||
int score = 0;
|
||||
int loops = stringToCheck.length() - 1;
|
||||
for (int i = 0; i <= loops; i++) {
|
||||
if (!Character.isLetter(stringToCheck.charAt(i)) &&
|
||||
!Character.isDigit(stringToCheck.charAt(i))) {
|
||||
score++;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for returning the count of numbers in a given String.
|
||||
*
|
||||
* @param stringToCheck
|
||||
* @return The numbers of digits in the String
|
||||
*/
|
||||
protected int getNumberCount(String stringToCheck) {
|
||||
int score = 0;
|
||||
int loops = stringToCheck.length() - 1;
|
||||
for (int i = 0; i <= loops; i++) {
|
||||
if (Character.isDigit(stringToCheck.charAt(i))) {
|
||||
score++;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the guides to show on the view.<br />
|
||||
* On the line style, the guides will show underneath<br />
|
||||
* On the rounded style, the guides will be shown on the outer edges.<br />
|
||||
* The view will be redrawn after the method is called.
|
||||
*
|
||||
* @param showGuides True if you want the guides to be shown
|
||||
*/
|
||||
public void setShowGuides(boolean showGuides) {
|
||||
mShowGuides = showGuides;
|
||||
if (mPassword != null && mPassword.length() > 0) {
|
||||
generatePasswordScore();
|
||||
} else {
|
||||
mCurrentScore = 0;
|
||||
}
|
||||
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the view is showing the guides for the password score
|
||||
*
|
||||
* @return True if the guides are being shown
|
||||
*/
|
||||
public boolean isShowingGuides() {
|
||||
return mShowGuides;
|
||||
}
|
||||
}
|
@ -296,7 +296,9 @@ public class ContactHelper {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Set<Long> deletedKeys = getRawContactMasterKeyIds(resolver);
|
||||
|
||||
// debugDeleteRawContacts(resolver);
|
||||
if (Constants.DEBUG_SYNC_REMOVE_CONTACTS) {
|
||||
debugDeleteRawContacts(resolver);
|
||||
}
|
||||
|
||||
// ContentProviderClient client = resolver.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
|
||||
// ContentValues values = new ContentValues();
|
||||
|
@ -20,11 +20,7 @@ package org.sufficientlysecure.keychain.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Constants.Pref;
|
||||
|
||||
@ -59,12 +55,8 @@ public class Preferences {
|
||||
}
|
||||
|
||||
public void updateSharedPreferences(Context context) {
|
||||
// multi-process preferences
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_MULTI_PROCESS);
|
||||
} else {
|
||||
mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE);
|
||||
}
|
||||
// multi-process safe preferences
|
||||
mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_MULTI_PROCESS);
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
@ -103,60 +95,6 @@ public class Preferences {
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public int getDefaultEncryptionAlgorithm() {
|
||||
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM,
|
||||
PGPEncryptedData.AES_256);
|
||||
}
|
||||
|
||||
public void setDefaultEncryptionAlgorithm(int value) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putInt(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM, value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public int getDefaultHashAlgorithm() {
|
||||
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM,
|
||||
HashAlgorithmTags.SHA256);
|
||||
}
|
||||
|
||||
public void setDefaultHashAlgorithm(int value) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public int getDefaultMessageCompression() {
|
||||
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION,
|
||||
CompressionAlgorithmTags.ZLIB);
|
||||
}
|
||||
|
||||
public void setDefaultMessageCompression(int value) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putInt(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION, value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public int getDefaultFileCompression() {
|
||||
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION,
|
||||
CompressionAlgorithmTags.UNCOMPRESSED);
|
||||
}
|
||||
|
||||
public void setDefaultFileCompression(int value) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public boolean getDefaultAsciiArmor() {
|
||||
return mSharedPreferences.getBoolean(Constants.Pref.DEFAULT_ASCII_ARMOR, false);
|
||||
}
|
||||
|
||||
public void setDefaultAsciiArmor(boolean value) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putBoolean(Constants.Pref.DEFAULT_ASCII_ARMOR, value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public boolean getCachedConsolidate() {
|
||||
return mSharedPreferences.getBoolean(Pref.CACHED_CONSOLIDATE, false);
|
||||
}
|
||||
@ -210,6 +148,7 @@ public class Preferences {
|
||||
}
|
||||
return servers.toArray(chunks);
|
||||
}
|
||||
|
||||
public String getPreferredKeyserver() {
|
||||
return getKeyServers()[0];
|
||||
}
|
||||
@ -231,21 +170,12 @@ public class Preferences {
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public void setWriteVersionHeader(boolean conceal) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putBoolean(Constants.Pref.WRITE_VERSION_HEADER, conceal);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public boolean getWriteVersionHeader() {
|
||||
return mSharedPreferences.getBoolean(Constants.Pref.WRITE_VERSION_HEADER, false);
|
||||
}
|
||||
|
||||
public void setSearchKeyserver(boolean searchKeyserver) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putBoolean(Pref.SEARCH_KEYSERVER, searchKeyserver);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public void setSearchKeybase(boolean searchKeybase) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putBoolean(Pref.SEARCH_KEYBASE, searchKeybase);
|
||||
@ -253,7 +183,7 @@ public class Preferences {
|
||||
}
|
||||
|
||||
public CloudSearchPrefs getCloudSearchPrefs() {
|
||||
return new CloudSearchPrefs(mSharedPreferences.getBoolean(Pref.SEARCH_KEYSERVER, true),
|
||||
return new CloudSearchPrefs(mSharedPreferences.getBoolean(Pref.SEARCH_KEYSERVER, true),
|
||||
mSharedPreferences.getBoolean(Pref.SEARCH_KEYBASE, true),
|
||||
getPreferredKeyserver());
|
||||
}
|
||||
@ -301,26 +231,9 @@ public class Preferences {
|
||||
|
||||
}
|
||||
setKeyServers(servers.toArray(new String[servers.size()]));
|
||||
|
||||
// migrate old uncompressed constant to new one
|
||||
if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, 0)
|
||||
== 0x21070001) {
|
||||
setDefaultFileCompression(CompressionAlgorithmTags.UNCOMPRESSED);
|
||||
}
|
||||
|
||||
// migrate away from MD5
|
||||
if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, 0)
|
||||
== HashAlgorithmTags.MD5) {
|
||||
setDefaultHashAlgorithm(HashAlgorithmTags.SHA256);
|
||||
}
|
||||
}
|
||||
// fall through
|
||||
case 4: {
|
||||
// for compatibility: change from SHA512 to SHA256
|
||||
if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, 0)
|
||||
== HashAlgorithmTags.SHA512) {
|
||||
setDefaultHashAlgorithm(HashAlgorithmTags.SHA256);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 669 B |
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_vpn_key_grey_24dp.png
Normal file
After Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 499 B |
BIN
OpenKeychain/src/main/res/drawable-mdpi/ic_vpn_key_grey_24dp.png
Normal file
After Width: | Height: | Size: 301 B |
Before Width: | Height: | Size: 827 B |
After Width: | Height: | Size: 463 B |
Before Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 714 B |
Before Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 825 B |
@ -8,13 +8,12 @@
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp">
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.AutoCorrectAutoCompleteTextView
|
||||
<org.sufficientlysecure.keychain.ui.widget.EmailEditText
|
||||
android:id="@+id/add_user_id_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/label_email"
|
||||
android:imeOptions="actionNext"
|
||||
android:inputType="textAutoCorrect|textEmailAddress"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.AutoCorrectAutoCompleteTextView
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:custom="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@ -42,13 +43,12 @@
|
||||
android:hint="@string/create_key_hint_full_name"
|
||||
android:ems="10" />
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.AutoCorrectAutoCompleteTextView
|
||||
<org.sufficientlysecure.keychain.ui.widget.EmailEditText
|
||||
android:id="@+id/create_key_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:imeOptions="actionNext"
|
||||
android:inputType="textAutoCorrect|textEmailAddress"
|
||||
android:hint="@string/label_email"
|
||||
android:ems="10" />
|
||||
|
||||
@ -58,17 +58,33 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_passphrase" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/create_key_passphrase"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:imeOptions="actionNext"
|
||||
android:inputType="textPassword"
|
||||
android:hint="@string/label_passphrase"
|
||||
android:ems="10"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.PasswordEditText
|
||||
android:id="@+id/create_key_passphrase"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:imeOptions="actionNext"
|
||||
android:hint="@string/label_passphrase"
|
||||
android:ems="10"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator.PasswordStrengthBarView
|
||||
android:id="@+id/create_key_passphrase_strength"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
custom:strength="medium"
|
||||
custom:showGuides="false"
|
||||
custom:color_fail="@color/android_red_light"
|
||||
custom:color_weak="@color/android_orange_light"
|
||||
custom:color_strong="@color/android_green_light" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/create_key_passphrase_again"
|
||||
|
@ -118,7 +118,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:text="Show"
|
||||
android:drawableRight="@drawable/ic_action_accounts"
|
||||
android:drawableRight="@drawable/ic_vpn_key_grey_24dp"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
style="@style/SelectableItem" />
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
@ -14,17 +15,35 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_no_passphrase" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/passphrase_passphrase"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:imeOptions="actionNext"
|
||||
android:inputType="textPassword"
|
||||
android:hint="@string/label_passphrase"
|
||||
android:ems="10"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.PasswordEditText
|
||||
android:id="@+id/passphrase_passphrase"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:imeOptions="actionNext"
|
||||
android:hint="@string/label_passphrase"
|
||||
android:ems="10"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator.PasswordStrengthBarView
|
||||
android:id="@+id/passphrase_repeat_passphrase_strength"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
custom:strength="medium"
|
||||
custom:showGuides="false"
|
||||
custom:color_fail="@color/android_red_light"
|
||||
custom:color_weak="@color/android_orange_light"
|
||||
custom:color_strong="@color/android_green_light" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/passphrase_passphrase_again"
|
||||
|
@ -12,4 +12,9 @@
|
||||
android:id="@+id/check_delete_after_encrypt"
|
||||
android:title="@string/label_delete_after_encryption"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/check_enable_compression"
|
||||
android:title="@string/label_enable_compression"
|
||||
android:checked="true"
|
||||
android:checkable="true" />
|
||||
</menu>
|
@ -4,4 +4,9 @@
|
||||
android:id="@+id/check_use_symmetric"
|
||||
android:title="@string/label_symmetric"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/check_enable_compression"
|
||||
android:title="@string/label_enable_compression"
|
||||
android:checked="true"
|
||||
android:checkable="true" />
|
||||
</menu>
|
@ -15,4 +15,18 @@
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
|
||||
<!-- Taken from Matt Allen Password Strength View
|
||||
https://github.com/matt-allen/android-password-strength-indicator -->
|
||||
<declare-styleable name="PasswordStrengthView">
|
||||
<attr name="strength" format="enum">
|
||||
<enum name="weak" value="0" />
|
||||
<enum name="medium" value="1" />
|
||||
<enum name="strong" value="2" />
|
||||
</attr>
|
||||
<attr name="showGuides" format="boolean" />
|
||||
<attr name="color_fail" format="color" />
|
||||
<attr name="color_weak" format="color" />
|
||||
<attr name="color_strong" format="color" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
@ -162,6 +162,7 @@
|
||||
<string name="expiry_date_dialog_title">"Set expiry date"</string>
|
||||
<string name="label_first_keyserver_is_used">"(First keyserver listed is preferred)"</string>
|
||||
<string name="label_preferred">"preferred"</string>
|
||||
<string name="label_enable_compression">"Enable compression"</string>
|
||||
|
||||
<string name="user_id_no_name">"<no name>"</string>
|
||||
<string name="none">"<none>"</string>
|
||||
@ -988,6 +989,7 @@
|
||||
<string name="msg_dc_clear_meta_size_unknown">"File size is unknown"</string>
|
||||
<string name="msg_dc_clear_meta_time">"Modification time: %s"</string>
|
||||
<string name="msg_dc_clear_signature_bad">"Signature check NOT OK!"</string>
|
||||
<string name="msg_dc_error_unsupported_hash_algo">"Unsupported and potentially insecure hash algorithm!"</string>
|
||||
<string name="msg_dc_clear_signature_check">"Verifying signature data"</string>
|
||||
<string name="msg_dc_clear_signature_ok">"Signature check OK"</string>
|
||||
<string name="msg_dc_clear_signature">"Saving signature data for later"</string>
|
||||
@ -1003,7 +1005,7 @@
|
||||
<string name="msg_dc_error_pgp_exception">"Encountered OpenPGP Exception during operation!"</string>
|
||||
<string name="msg_dc_integrity_check_ok">"Integrity check OK!"</string>
|
||||
<string name="msg_dc_ok_meta_only">"Only metadata was requested, skipping decryption"</string>
|
||||
<string name="msg_dc_ok">"OK"</string>
|
||||
<string name="msg_dc_ok">"Decryption/Verification finished"</string>
|
||||
<string name="msg_dc_pass_cached">"Using passphrase from cache"</string>
|
||||
<string name="msg_dc_pending_nfc">"NFC token required, requesting user input…"</string>
|
||||
<string name="msg_dc_pending_passphrase">"Passphrase required, requesting user input…"</string>
|
||||
@ -1015,6 +1017,7 @@
|
||||
<string name="msg_dc_trail_sym">"Encountered trailing, symmetrically encrypted data"</string>
|
||||
<string name="msg_dc_trail_unknown">"Encountered trailing data of unknown type"</string>
|
||||
<string name="msg_dc_unlocking">"Unlocking secret key"</string>
|
||||
<string name="msg_dc_old_symmetric_encryption_algo">"Potentially insecure encryption algorithm has been used!"</string>
|
||||
|
||||
<!-- Messages for VerifySignedLiteralData operation -->
|
||||
<string name="msg_vl">"Starting signature check"</string>
|
||||
@ -1069,11 +1072,14 @@
|
||||
<string name="msg_pse_symmetric">"Preparing symmetric encryption"</string>
|
||||
|
||||
<string name="msg_crt_certifying">"Generating certifications"</string>
|
||||
<string name="msg_crt_certify_all">"Certifying all user IDs for key %s"</string>
|
||||
<plurals name="msg_crt_certify_some">
|
||||
<plurals name="msg_crt_certify_uids">
|
||||
<item quantity="one">"Certifying one user ID for key %2$s"</item>
|
||||
<item quantity="other">"Certifying %1$d user IDs for key %2$s"</item>
|
||||
</plurals>
|
||||
<plurals name="msg_crt_certify_uats">
|
||||
<item quantity="one">"Certifying one user attribute for key %2$s"</item>
|
||||
<item quantity="other">"Certifying %1$d user attributes for key %2$s"</item>
|
||||
</plurals>
|
||||
<string name="msg_crt_error_self">"Cannot issue self-certificate like this!"</string>
|
||||
<string name="msg_crt_error_master_not_found">"Master key not found!"</string>
|
||||
<string name="msg_crt_error_nothing">"No keys certified!"</string>
|
||||
|
@ -1,52 +1,23 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory android:title="@string/section_defaults">
|
||||
<CheckBoxPreference
|
||||
android:key="writeVersionHeader"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_write_version_header"
|
||||
android:summary="@string/label_write_version_header_summary" />
|
||||
<org.sufficientlysecure.keychain.ui.widget.IntegerListPreference
|
||||
android:key="defaultEncryptionAlgorithm"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_encryption_algorithm" />
|
||||
<org.sufficientlysecure.keychain.ui.widget.IntegerListPreference
|
||||
android:key="defaultHashAlgorithm"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_hash_algorithm" />
|
||||
<org.sufficientlysecure.keychain.ui.widget.IntegerListPreference
|
||||
android:key="defaultMessageCompression"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_message_compression" />
|
||||
<org.sufficientlysecure.keychain.ui.widget.IntegerListPreference
|
||||
android:key="defaultFileCompression"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_file_compression" />
|
||||
<CheckBoxPreference
|
||||
android:key="defaultAsciiArmor"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_ascii_armor" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/section_passphrase_cache">
|
||||
<org.sufficientlysecure.keychain.ui.widget.IntegerListPreference
|
||||
android:entries="@array/passphrase_cache_ttl_entries"
|
||||
android:entryValues="@array/passphrase_cache_ttl_values"
|
||||
android:key="passphraseCacheTtl"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_passphrase_cache_ttl" />
|
||||
<CheckBoxPreference
|
||||
android:key="passphraseCacheSubs"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_passphrase_cache_subs" />
|
||||
<CheckBoxPreference
|
||||
android:key="useDefaultYubikeyPin"
|
||||
android:persistent="false"
|
||||
android:defaultValue="true"
|
||||
android:title="@string/label_use_default_yubikey_pin"
|
||||
android:summary="@string/label_label_use_default_yubikey_pin_summary" />
|
||||
<CheckBoxPreference
|
||||
android:key="useNumKeypadForYubikeyPin"
|
||||
android:persistent="false"
|
||||
android:defaultValue="false"
|
||||
android:title="@string/label_use_num_keypad_for_yubikey_pin" />
|
||||
</PreferenceCategory>
|
||||
<org.sufficientlysecure.keychain.ui.widget.IntegerListPreference
|
||||
android:entries="@array/passphrase_cache_ttl_entries"
|
||||
android:entryValues="@array/passphrase_cache_ttl_values"
|
||||
android:key="passphraseCacheTtl"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_passphrase_cache_ttl" />
|
||||
<CheckBoxPreference
|
||||
android:key="passphraseCacheSubs"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_passphrase_cache_subs" />
|
||||
<CheckBoxPreference
|
||||
android:key="useDefaultYubikeyPin"
|
||||
android:persistent="false"
|
||||
android:defaultValue="true"
|
||||
android:title="@string/label_use_default_yubikey_pin"
|
||||
android:summary="@string/label_label_use_default_yubikey_pin_summary" />
|
||||
<CheckBoxPreference
|
||||
android:key="useNumKeypadForYubikeyPin"
|
||||
android:persistent="false"
|
||||
android:defaultValue="false"
|
||||
android:title="@string/label_use_num_keypad_for_yubikey_pin" />
|
||||
</PreferenceScreen>
|
||||
|
@ -4,5 +4,5 @@
|
||||
android:title="@string/section_cloud_search" />
|
||||
<header
|
||||
android:fragment="org.sufficientlysecure.keychain.ui.SettingsActivity$AdvancedPrefsFragment"
|
||||
android:title="@string/section_advanced" />
|
||||
android:title="@string/section_passphrase_cache" />
|
||||
</preference-headers>
|
||||
|