Merge pull request #2097 from open-keychain/security-problems
Security Warnings API
This commit is contained in:
@@ -19,16 +19,12 @@
|
||||
package org.sufficientlysecure.keychain.operations.results;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
||||
import org.openintents.openpgp.OpenPgpMetadata;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
|
||||
@@ -40,7 +36,7 @@ public class DecryptVerifyResult extends InputPendingResult {
|
||||
OpenPgpSignatureResult mSignatureResult;
|
||||
OpenPgpDecryptionResult mDecryptionResult;
|
||||
OpenPgpMetadata mDecryptionMetadata;
|
||||
ArrayList<SecurityProblem> mSecurityProblems;
|
||||
DecryptVerifySecurityProblem mSecurityProblem;
|
||||
|
||||
CryptoInputParcel mCachedCryptoInputParcel;
|
||||
|
||||
@@ -73,7 +69,7 @@ public class DecryptVerifyResult extends InputPendingResult {
|
||||
mCachedCryptoInputParcel = source.readParcelable(CryptoInputParcel.class.getClassLoader());
|
||||
mSkippedDisallowedKeys = source.createLongArray();
|
||||
|
||||
mSecurityProblems = (ArrayList<SecurityProblem>) source.readSerializable();
|
||||
mSecurityProblem = (DecryptVerifySecurityProblem) source.readSerializable();
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +133,7 @@ public class DecryptVerifyResult extends InputPendingResult {
|
||||
dest.writeParcelable(mCachedCryptoInputParcel, flags);
|
||||
dest.writeLongArray(mSkippedDisallowedKeys);
|
||||
|
||||
dest.writeSerializable(mSecurityProblems);
|
||||
dest.writeSerializable(mSecurityProblem);
|
||||
}
|
||||
|
||||
public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() {
|
||||
@@ -150,28 +146,11 @@ public class DecryptVerifyResult extends InputPendingResult {
|
||||
}
|
||||
};
|
||||
|
||||
public void addSecurityProblem(SecurityProblem securityProblem) {
|
||||
if (securityProblem == null) {
|
||||
return;
|
||||
}
|
||||
if (mSecurityProblems == null) {
|
||||
mSecurityProblems = new ArrayList<>();
|
||||
}
|
||||
mSecurityProblems.add(securityProblem);
|
||||
public DecryptVerifySecurityProblem getSecurityProblem() {
|
||||
return mSecurityProblem;
|
||||
}
|
||||
|
||||
public void addSecurityProblems(List<SecurityProblem> securityProblems) {
|
||||
if (securityProblems == null) {
|
||||
return;
|
||||
}
|
||||
if (mSecurityProblems == null) {
|
||||
mSecurityProblems = new ArrayList<>();
|
||||
}
|
||||
mSecurityProblems.addAll(securityProblems);
|
||||
}
|
||||
|
||||
public List<SecurityProblem> getSecurityProblems() {
|
||||
return mSecurityProblems != null ?
|
||||
Collections.unmodifiableList(mSecurityProblems) : Collections.<SecurityProblem>emptyList();
|
||||
public void setSecurityProblemResult(DecryptVerifySecurityProblem securityProblem) {
|
||||
mSecurityProblem = securityProblem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Vincent Breitmoser <look@my.amazin.horse>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureSigningAlgorithm;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmProblem;
|
||||
|
||||
|
||||
public class DecryptVerifySecurityProblem implements Serializable {
|
||||
public final KeySecurityProblem encryptionKeySecurityProblem;
|
||||
public final KeySecurityProblem signingKeySecurityProblem;
|
||||
public final EncryptionAlgorithmProblem symmetricSecurityProblem;
|
||||
public final InsecureSigningAlgorithm signatureSecurityProblem;
|
||||
|
||||
private DecryptVerifySecurityProblem(DecryptVerifySecurityProblemBuilder builder) {
|
||||
encryptionKeySecurityProblem = builder.encryptionKeySecurityProblem;
|
||||
signingKeySecurityProblem = builder.signingKeySecurityProblem;
|
||||
symmetricSecurityProblem = builder.symmetricSecurityProblem;
|
||||
signatureSecurityProblem = builder.signatureSecurityProblem;
|
||||
}
|
||||
|
||||
public SecurityProblem getPrioritySecurityProblem() {
|
||||
if (encryptionKeySecurityProblem != null) {
|
||||
return encryptionKeySecurityProblem;
|
||||
} else if (signingKeySecurityProblem != null) {
|
||||
return signingKeySecurityProblem;
|
||||
} else if (symmetricSecurityProblem != null) {
|
||||
return symmetricSecurityProblem;
|
||||
} else if (signatureSecurityProblem != null) {
|
||||
return signatureSecurityProblem;
|
||||
} else {
|
||||
throw new IllegalStateException("No security problem?");
|
||||
}
|
||||
}
|
||||
|
||||
static class DecryptVerifySecurityProblemBuilder {
|
||||
private KeySecurityProblem encryptionKeySecurityProblem;
|
||||
private KeySecurityProblem signingKeySecurityProblem;
|
||||
private EncryptionAlgorithmProblem symmetricSecurityProblem;
|
||||
private InsecureSigningAlgorithm signatureSecurityProblem;
|
||||
|
||||
void addEncryptionKeySecurityProblem(KeySecurityProblem encryptionKeySecurityProblem) {
|
||||
this.encryptionKeySecurityProblem = encryptionKeySecurityProblem;
|
||||
}
|
||||
|
||||
void addSigningKeyProblem(KeySecurityProblem keySecurityProblem) {
|
||||
this.signingKeySecurityProblem = keySecurityProblem;
|
||||
}
|
||||
|
||||
void addSymmetricSecurityProblem(EncryptionAlgorithmProblem symmetricSecurityProblem) {
|
||||
this.symmetricSecurityProblem = symmetricSecurityProblem;
|
||||
}
|
||||
|
||||
void addSignatureSecurityProblem(InsecureSigningAlgorithm signatureSecurityProblem) {
|
||||
this.signatureSecurityProblem = signatureSecurityProblem;
|
||||
}
|
||||
|
||||
public DecryptVerifySecurityProblem build() {
|
||||
if (encryptionKeySecurityProblem == null && signingKeySecurityProblem == null &&
|
||||
symmetricSecurityProblem == null && signatureSecurityProblem == null) {
|
||||
return null;
|
||||
}
|
||||
return new DecryptVerifySecurityProblem(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,33 +17,20 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.SymmetricAlgorithmProblem;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
public class OpenPgpDecryptionResultBuilder {
|
||||
class OpenPgpDecryptionResultBuilder {
|
||||
|
||||
// builder
|
||||
private boolean isInsecure = false;
|
||||
private boolean isEncrypted = false;
|
||||
private byte[] sessionKey;
|
||||
private byte[] decryptedSessionKey;
|
||||
private ArrayList<SecurityProblem> securityProblems;
|
||||
|
||||
public void addSecurityProblem(SecurityProblem securityProblem) {
|
||||
if (securityProblems == null) {
|
||||
securityProblems = new ArrayList<>();
|
||||
}
|
||||
securityProblems.add(securityProblem);
|
||||
}
|
||||
|
||||
public List<SecurityProblem> getKeySecurityProblems() {
|
||||
return securityProblems != null ? Collections.unmodifiableList(securityProblems) : null;
|
||||
public void setInsecure(boolean insecure) {
|
||||
this.isInsecure = insecure;
|
||||
}
|
||||
|
||||
public void setEncrypted(boolean encrypted) {
|
||||
@@ -51,15 +38,14 @@ public class OpenPgpDecryptionResultBuilder {
|
||||
}
|
||||
|
||||
public OpenPgpDecryptionResult build() {
|
||||
if (securityProblems != null && !securityProblems.isEmpty()) {
|
||||
if (isInsecure) {
|
||||
Log.d(Constants.TAG, "RESULT_INSECURE");
|
||||
return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_INSECURE, sessionKey, decryptedSessionKey);
|
||||
}
|
||||
|
||||
if (isEncrypted) {
|
||||
Log.d(Constants.TAG, "RESULT_ENCRYPTED");
|
||||
return new OpenPgpDecryptionResult(
|
||||
OpenPgpDecryptionResult.RESULT_ENCRYPTED, sessionKey, decryptedSessionKey);
|
||||
return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED, sessionKey, decryptedSessionKey);
|
||||
}
|
||||
|
||||
Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED");
|
||||
@@ -67,12 +53,11 @@ public class OpenPgpDecryptionResultBuilder {
|
||||
}
|
||||
|
||||
|
||||
public void setSessionKey(byte[] sessionKey, byte[] decryptedSessionKey) {
|
||||
void setSessionKey(byte[] sessionKey, byte[] decryptedSessionKey) {
|
||||
if ((sessionKey == null) != (decryptedSessionKey == null)) {
|
||||
throw new AssertionError("sessionKey must be null iff decryptedSessionKey is null!");
|
||||
}
|
||||
this.sessionKey = sessionKey;
|
||||
this.decryptedSessionKey = decryptedSessionKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,9 +19,7 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult.SenderStatusResult;
|
||||
@@ -55,9 +53,9 @@ public class OpenPgpSignatureResultBuilder {
|
||||
private boolean mIsSignatureKeyCertified = false;
|
||||
private boolean mIsKeyRevoked = false;
|
||||
private boolean mIsKeyExpired = false;
|
||||
private boolean mInsecure = false;
|
||||
private String mSenderAddress;
|
||||
private Date mSignatureTimestamp;
|
||||
private ArrayList<SecurityProblem> mSecurityProblems;
|
||||
|
||||
public OpenPgpSignatureResultBuilder(KeyRepository keyRepository) {
|
||||
this.mKeyRepository = keyRepository;
|
||||
@@ -83,15 +81,8 @@ public class OpenPgpSignatureResultBuilder {
|
||||
this.mValidSignature = validSignature;
|
||||
}
|
||||
|
||||
public void addSecurityProblem(SecurityProblem securityProblem) {
|
||||
if (mSecurityProblems == null) {
|
||||
mSecurityProblems = new ArrayList<>();
|
||||
}
|
||||
mSecurityProblems.add(securityProblem);
|
||||
}
|
||||
|
||||
public List<SecurityProblem> getSecurityProblems() {
|
||||
return mSecurityProblems != null ? Collections.unmodifiableList(mSecurityProblems) : null;
|
||||
public void setInsecure(boolean insecure) {
|
||||
this.mInsecure = insecure;
|
||||
}
|
||||
|
||||
public void setSignatureKeyCertified(boolean isSignatureKeyCertified) {
|
||||
@@ -115,6 +106,10 @@ public class OpenPgpSignatureResultBuilder {
|
||||
this.mConfirmedUserIds = confirmedUserIds;
|
||||
}
|
||||
|
||||
public boolean isInsecure() {
|
||||
return mInsecure;
|
||||
}
|
||||
|
||||
public void initValid(CanonicalizedPublicKey signingKey) {
|
||||
setSignatureAvailable(true);
|
||||
setKnownKey(true);
|
||||
@@ -189,7 +184,7 @@ public class OpenPgpSignatureResultBuilder {
|
||||
} else if (mIsKeyExpired) {
|
||||
Log.d(Constants.TAG, "RESULT_INVALID_KEY_EXPIRED");
|
||||
signatureStatus = OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED;
|
||||
} else if (mSecurityProblems != null && !mSecurityProblems.isEmpty()) {
|
||||
} else if (mInsecure) {
|
||||
Log.d(Constants.TAG, "RESULT_INVALID_INSECURE");
|
||||
signatureStatus = OpenPgpSignatureResult.RESULT_INVALID_KEY_INSECURE;
|
||||
} else if (mIsSignatureKeyCertified) {
|
||||
|
||||
@@ -68,9 +68,11 @@ import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem.DecryptVerifySecurityProblemBuilder;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureBitStrength;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.SymmetricAlgorithmProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||
@@ -301,6 +303,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
JcaSkipMarkerPGPObjectFactory plainFact;
|
||||
Object dataChunk;
|
||||
EncryptStreamResult esResult = null;
|
||||
DecryptVerifySecurityProblemBuilder securityProblemBuilder = new DecryptVerifySecurityProblemBuilder();
|
||||
{ // resolve encrypted (symmetric and asymmetric) packets
|
||||
JcaSkipMarkerPGPObjectFactory pgpF = new JcaSkipMarkerPGPObjectFactory(in);
|
||||
Object obj = pgpF.nextObject();
|
||||
@@ -322,15 +325,18 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
|
||||
if (esResult.encryptionKeySecurityProblem != null) {
|
||||
log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
|
||||
decryptionResultBuilder.addSecurityProblem(esResult.encryptionKeySecurityProblem);
|
||||
securityProblemBuilder.addEncryptionKeySecurityProblem(esResult.encryptionKeySecurityProblem);
|
||||
decryptionResultBuilder.setInsecure(true);
|
||||
}
|
||||
|
||||
// Check for insecure encryption algorithms!
|
||||
SymmetricAlgorithmProblem symmetricSecurityProblem =
|
||||
PgpSecurityConstants.checkSecureSymmetricAlgorithm(esResult.symmetricEncryptionAlgo);
|
||||
EncryptionAlgorithmProblem symmetricSecurityProblem =
|
||||
PgpSecurityConstants.checkSecureSymmetricAlgorithm(
|
||||
esResult.symmetricEncryptionAlgo, esResult.sessionKey);
|
||||
if (symmetricSecurityProblem != null) {
|
||||
log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
|
||||
decryptionResultBuilder.addSecurityProblem(symmetricSecurityProblem);
|
||||
securityProblemBuilder.addSymmetricSecurityProblem(symmetricSecurityProblem);
|
||||
decryptionResultBuilder.setInsecure(true);
|
||||
}
|
||||
|
||||
plainFact = new JcaSkipMarkerPGPObjectFactory(esResult.cleartextStream);
|
||||
@@ -361,7 +367,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
plainFact = fact;
|
||||
}
|
||||
|
||||
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mKeyRepository, input.getSenderAddress());
|
||||
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(
|
||||
mKeyRepository, input.getSenderAddress(), securityProblemBuilder);
|
||||
if (signatureChecker.initializeOnePassSignature(dataChunk, log, indent +1)) {
|
||||
dataChunk = plainFact.nextObject();
|
||||
}
|
||||
@@ -536,7 +543,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
// Handle missing integrity protection like failed integrity protection!
|
||||
// The MDC packet can be stripped by an attacker!
|
||||
log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent);
|
||||
decryptionResultBuilder.addSecurityProblem(new MissingMdc());
|
||||
securityProblemBuilder.addSymmetricSecurityProblem(new MissingMdc(esResult.sessionKey));
|
||||
decryptionResultBuilder.setInsecure(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,8 +557,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
result.setCachedCryptoInputParcel(cryptoInput);
|
||||
result.setSignatureResult(signatureChecker.getSignatureResult());
|
||||
result.setDecryptionResult(decryptionResultBuilder.build());
|
||||
result.addSecurityProblems(signatureChecker.getSecurityProblems());
|
||||
result.addSecurityProblems(decryptionResultBuilder.getKeySecurityProblems());
|
||||
result.setSecurityProblemResult(securityProblemBuilder.build());
|
||||
result.setDecryptionMetadata(metadata);
|
||||
result.mOperationTime = opTime;
|
||||
|
||||
@@ -888,7 +895,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
updateProgress(R.string.progress_processing_signature, 60, 100);
|
||||
JcaSkipMarkerPGPObjectFactory pgpFact = new JcaSkipMarkerPGPObjectFactory(aIn);
|
||||
|
||||
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mKeyRepository, input.getSenderAddress());
|
||||
DecryptVerifySecurityProblemBuilder securityProblemBuilder = new DecryptVerifySecurityProblemBuilder();
|
||||
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mKeyRepository, input.getSenderAddress(),
|
||||
securityProblemBuilder);
|
||||
|
||||
Object o = pgpFact.nextObject();
|
||||
if (!signatureChecker.initializeSignature(o, log, indent+1)) {
|
||||
@@ -919,6 +928,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
result.setSignatureResult(signatureChecker.getSignatureResult());
|
||||
result.setDecryptionResult(
|
||||
new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
|
||||
result.setSecurityProblemResult(securityProblemBuilder.build());
|
||||
result.setDecryptionMetadata(metadata);
|
||||
return result;
|
||||
}
|
||||
@@ -943,7 +953,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
o = pgpFact.nextObject();
|
||||
}
|
||||
|
||||
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mKeyRepository, input.getSenderAddress());
|
||||
DecryptVerifySecurityProblemBuilder securityProblemBuilder = new DecryptVerifySecurityProblemBuilder();
|
||||
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mKeyRepository, input.getSenderAddress(),
|
||||
securityProblemBuilder);
|
||||
|
||||
if ( ! signatureChecker.initializeSignature(o, log, indent+1)) {
|
||||
log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0);
|
||||
@@ -994,6 +1006,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
|
||||
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
||||
result.setSignatureResult(signatureChecker.getSignatureResult());
|
||||
result.setSecurityProblemResult(securityProblemBuilder.build());
|
||||
result.setDecryptionResult(
|
||||
new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
|
||||
return result;
|
||||
|
||||
@@ -31,11 +31,11 @@ import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
|
||||
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
import org.bouncycastle.crypto.ec.CustomNamedCurves;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureBitStrength;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureHashAlgorithm;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureSymmetricAlgorithm;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureSigningAlgorithm;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureEncryptionAlgorithm;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.NotWhitelistedCurve;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.SymmetricAlgorithmProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.UnidentifiedKeyProblem;
|
||||
|
||||
|
||||
@@ -74,9 +74,9 @@ public class PgpSecurityConstants {
|
||||
// CAMELLIA_256: not used widely
|
||||
));
|
||||
|
||||
public static SymmetricAlgorithmProblem checkSecureSymmetricAlgorithm(int id) {
|
||||
public static EncryptionAlgorithmProblem checkSecureSymmetricAlgorithm(int id, byte[] sessionKey) {
|
||||
if (!sSymmetricAlgorithmsWhitelist.contains(id)) {
|
||||
return new InsecureSymmetricAlgorithm(id);
|
||||
return new InsecureEncryptionAlgorithm(sessionKey, id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -107,9 +107,9 @@ public class PgpSecurityConstants {
|
||||
// SHA224: Not used widely, Yahoo argues against it
|
||||
));
|
||||
|
||||
public static InsecureHashAlgorithm checkSignatureAlgorithmForSecurityProblems(int hashAlgorithm) {
|
||||
static InsecureSigningAlgorithm checkSignatureAlgorithmForSecurityProblems(int hashAlgorithm) {
|
||||
if (!sHashAlgorithmsWhitelist.contains(hashAlgorithm)) {
|
||||
return new InsecureHashAlgorithm(hashAlgorithm);
|
||||
return new InsecureSigningAlgorithm(hashAlgorithm);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -27,28 +27,26 @@ import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
|
||||
public class PgpSignEncryptData implements Parcelable {
|
||||
|
||||
protected String mVersionHeader = null;
|
||||
protected boolean mEnableAsciiArmorOutput = false;
|
||||
protected int mCompressionAlgorithm = CompressionAlgorithmTags.UNCOMPRESSED;
|
||||
protected long[] mEncryptionMasterKeyIds = null;
|
||||
protected Passphrase mSymmetricPassphrase = null;
|
||||
protected int mSymmetricEncryptionAlgorithm = PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT;
|
||||
protected long mSignatureMasterKeyId = Constants.key.none;
|
||||
protected Long mSignatureSubKeyId = null;
|
||||
protected int mSignatureHashAlgorithm = PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT;
|
||||
protected long mAdditionalEncryptId = Constants.key.none;
|
||||
protected String mCharset;
|
||||
protected boolean mCleartextSignature;
|
||||
protected boolean mDetachedSignature = false;
|
||||
protected boolean mHiddenRecipients = false;
|
||||
protected boolean mIntegrityProtected = true;
|
||||
protected boolean mAddBackupHeader = false;
|
||||
private String mVersionHeader = null;
|
||||
private boolean mEnableAsciiArmorOutput = false;
|
||||
private int mCompressionAlgorithm = CompressionAlgorithmTags.UNCOMPRESSED;
|
||||
private long[] mEncryptionMasterKeyIds = null;
|
||||
private Passphrase mSymmetricPassphrase = null;
|
||||
private int mSymmetricEncryptionAlgorithm = PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT;
|
||||
private long mSignatureMasterKeyId = Constants.key.none;
|
||||
private Long mSignatureSubKeyId = null;
|
||||
private int mSignatureHashAlgorithm = PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT;
|
||||
private long mAdditionalEncryptId = Constants.key.none;
|
||||
private String mCharset;
|
||||
private boolean mCleartextSignature;
|
||||
private boolean mDetachedSignature = false;
|
||||
private boolean mHiddenRecipients = false;
|
||||
private boolean mAddBackupHeader = false;
|
||||
|
||||
public PgpSignEncryptData(){
|
||||
}
|
||||
|
||||
PgpSignEncryptData(Parcel source) {
|
||||
private PgpSignEncryptData(Parcel source) {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
|
||||
mVersionHeader = source.readString();
|
||||
@@ -65,7 +63,6 @@ public class PgpSignEncryptData implements Parcelable {
|
||||
mCleartextSignature = source.readInt() == 1;
|
||||
mDetachedSignature = source.readInt() == 1;
|
||||
mHiddenRecipients = source.readInt() == 1;
|
||||
mIntegrityProtected = source.readInt() == 1;
|
||||
mAddBackupHeader = source.readInt() == 1;
|
||||
}
|
||||
|
||||
@@ -95,7 +92,6 @@ public class PgpSignEncryptData implements Parcelable {
|
||||
dest.writeInt(mCleartextSignature ? 1 : 0);
|
||||
dest.writeInt(mDetachedSignature ? 1 : 0);
|
||||
dest.writeInt(mHiddenRecipients ? 1 : 0);
|
||||
dest.writeInt(mIntegrityProtected ? 1 : 0);
|
||||
dest.writeInt(mAddBackupHeader ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -220,18 +216,6 @@ public class PgpSignEncryptData implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isIntegrityProtected() {
|
||||
return mIntegrityProtected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only use for testing! Never disable integrity protection!
|
||||
*/
|
||||
public PgpSignEncryptData setIntegrityProtected(boolean integrityProtected) {
|
||||
this.mIntegrityProtected = integrityProtected;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PgpSignEncryptData setAddBackupHeader(boolean addBackupHeader) {
|
||||
this.mAddBackupHeader = addBackupHeader;
|
||||
return this;
|
||||
|
||||
@@ -321,7 +321,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
|
||||
JcePGPDataEncryptorBuilder encryptorBuilder =
|
||||
new JcePGPDataEncryptorBuilder(algo)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
|
||||
.setWithIntegrityPacket(data.isIntegrityProtected());
|
||||
.setWithIntegrityPacket(true);
|
||||
|
||||
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.SignatureException;
|
||||
import java.util.List;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPOnePassSignature;
|
||||
@@ -37,11 +36,12 @@ import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureHashAlgorithm;
|
||||
import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem.DecryptVerifySecurityProblemBuilder;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureSigningAlgorithm;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
|
||||
@@ -52,20 +52,24 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
class PgpSignatureChecker {
|
||||
|
||||
private final OpenPgpSignatureResultBuilder signatureResultBuilder;
|
||||
private final DecryptVerifySecurityProblemBuilder securityProblemBuilder;
|
||||
|
||||
private CanonicalizedPublicKey signingKey;
|
||||
|
||||
private int signatureIndex;
|
||||
PGPOnePassSignature onePassSignature;
|
||||
PGPSignature signature;
|
||||
private PGPOnePassSignature onePassSignature;
|
||||
private PGPSignature signature;
|
||||
|
||||
KeyRepository mKeyRepository;
|
||||
private KeyRepository mKeyRepository;
|
||||
|
||||
PgpSignatureChecker(KeyRepository keyRepository, String senderAddress) {
|
||||
PgpSignatureChecker(KeyRepository keyRepository, String senderAddress,
|
||||
DecryptVerifySecurityProblemBuilder securityProblemBuilder) {
|
||||
mKeyRepository = keyRepository;
|
||||
|
||||
signatureResultBuilder = new OpenPgpSignatureResultBuilder(keyRepository);
|
||||
signatureResultBuilder.setSenderAddress(senderAddress);
|
||||
|
||||
this.securityProblemBuilder = securityProblemBuilder;
|
||||
}
|
||||
|
||||
boolean initializeSignature(Object dataChunk, OperationLog log, int indent) throws PGPException {
|
||||
@@ -142,11 +146,12 @@ class PgpSignatureChecker {
|
||||
PgpSecurityConstants.checkForSecurityProblems(signingKey);
|
||||
if (keySecurityProblem != null) {
|
||||
log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
|
||||
signatureResultBuilder.addSecurityProblem(keySecurityProblem);
|
||||
securityProblemBuilder.addSigningKeyProblem(keySecurityProblem);
|
||||
signatureResultBuilder.setInsecure(true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
boolean isInitialized() {
|
||||
return signingKey != null;
|
||||
}
|
||||
|
||||
@@ -173,7 +178,7 @@ class PgpSignatureChecker {
|
||||
}
|
||||
}
|
||||
|
||||
public void findAvailableSignature(PGPSignatureList sigList) {
|
||||
private void findAvailableSignature(PGPSignatureList sigList) {
|
||||
// go through all signatures (should be just one), make sure we have
|
||||
// the key and it matches the one we’re looking for
|
||||
for (int i = 0; i < sigList.size(); ++i) {
|
||||
@@ -238,11 +243,12 @@ class PgpSignatureChecker {
|
||||
}
|
||||
|
||||
// check for insecure hash algorithms
|
||||
InsecureHashAlgorithm signatureSecurityProblem =
|
||||
InsecureSigningAlgorithm signatureSecurityProblem =
|
||||
PgpSecurityConstants.checkSignatureAlgorithmForSecurityProblems(signature.getHashAlgorithm());
|
||||
if (signatureSecurityProblem != null) {
|
||||
log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
|
||||
signatureResultBuilder.addSecurityProblem(signatureSecurityProblem);
|
||||
securityProblemBuilder.addSignatureSecurityProblem(signatureSecurityProblem);
|
||||
signatureResultBuilder.setInsecure(true);
|
||||
}
|
||||
|
||||
signatureResultBuilder.setSignatureTimestamp(signature.getCreationTime());
|
||||
@@ -275,11 +281,12 @@ class PgpSignatureChecker {
|
||||
}
|
||||
|
||||
// check for insecure hash algorithms
|
||||
InsecureHashAlgorithm signatureSecurityProblem =
|
||||
InsecureSigningAlgorithm signatureSecurityProblem =
|
||||
PgpSecurityConstants.checkSignatureAlgorithmForSecurityProblems(onePassSignature.getHashAlgorithm());
|
||||
if (signatureSecurityProblem != null) {
|
||||
log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
|
||||
signatureResultBuilder.addSecurityProblem(signatureSecurityProblem);
|
||||
securityProblemBuilder.addSignatureSecurityProblem(signatureSecurityProblem);
|
||||
signatureResultBuilder.setInsecure(true);
|
||||
}
|
||||
|
||||
signatureResultBuilder.setSignatureTimestamp(messageSignature.getCreationTime());
|
||||
@@ -297,10 +304,6 @@ class PgpSignatureChecker {
|
||||
return signatureResultBuilder.build();
|
||||
}
|
||||
|
||||
public List<SecurityProblem> getSecurityProblems() {
|
||||
return signatureResultBuilder.getSecurityProblems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
|
||||
*/
|
||||
|
||||
@@ -18,10 +18,40 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
|
||||
|
||||
public abstract class SecurityProblem implements Serializable {
|
||||
|
||||
public String getIdentifier() {
|
||||
if (!isIdentifiable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(out);
|
||||
oos.writeObject(this);
|
||||
oos.close();
|
||||
|
||||
byte[] digest = MessageDigest.getInstance("SHA1").digest(out.toByteArray());
|
||||
return Base64.toBase64String(digest);
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isIdentifiable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static abstract class KeySecurityProblem extends SecurityProblem {
|
||||
public final long masterKeyId;
|
||||
public final long subKeyId;
|
||||
@@ -32,10 +62,25 @@ public abstract class SecurityProblem implements Serializable {
|
||||
this.subKeyId = subKeyId;
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdentifiable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class SymmetricAlgorithmProblem extends SecurityProblem {
|
||||
public static abstract class EncryptionAlgorithmProblem extends SecurityProblem {
|
||||
@SuppressWarnings("unused") // used for identifying this specific problem
|
||||
private final byte[] sessionKey;
|
||||
|
||||
private EncryptionAlgorithmProblem(byte[] sessionKey) {
|
||||
this.sessionKey = sessionKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdentifiable() {
|
||||
return sessionKey != null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InsecureBitStrength extends KeySecurityProblem {
|
||||
@@ -62,23 +107,26 @@ public abstract class SecurityProblem implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public static class InsecureHashAlgorithm extends SecurityProblem {
|
||||
public static class InsecureSigningAlgorithm extends SecurityProblem {
|
||||
public final int hashAlgorithm;
|
||||
|
||||
InsecureHashAlgorithm(int hashAlgorithm) {
|
||||
InsecureSigningAlgorithm(int hashAlgorithm) {
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InsecureSymmetricAlgorithm extends SymmetricAlgorithmProblem {
|
||||
public static class InsecureEncryptionAlgorithm extends EncryptionAlgorithmProblem {
|
||||
public final int symmetricAlgorithm;
|
||||
|
||||
InsecureSymmetricAlgorithm(int symmetricAlgorithm) {
|
||||
InsecureEncryptionAlgorithm(byte[] sessionKey, int symmetricAlgorithm) {
|
||||
super(sessionKey);
|
||||
this.symmetricAlgorithm = symmetricAlgorithm;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MissingMdc extends SymmetricAlgorithmProblem {
|
||||
|
||||
public static class MissingMdc extends EncryptionAlgorithmProblem {
|
||||
MissingMdc(byte[] sessionKey) {
|
||||
super(sessionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,10 @@ public class KeychainContract {
|
||||
String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name
|
||||
}
|
||||
|
||||
interface OverriddenWarnings {
|
||||
String IDENTIFIER = "identifier";
|
||||
}
|
||||
|
||||
public static final String CONTENT_AUTHORITY = Constants.PROVIDER_AUTHORITY;
|
||||
|
||||
private static final Uri BASE_CONTENT_URI_INTERNAL = Uri
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.OverriddenWarnings;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeysColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
|
||||
import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity;
|
||||
@@ -51,7 +52,7 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
*/
|
||||
public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
private static final String DATABASE_NAME = "openkeychain.db";
|
||||
private static final int DATABASE_VERSION = 20;
|
||||
private static final int DATABASE_VERSION = 21;
|
||||
private Context mContext;
|
||||
|
||||
public interface Tables {
|
||||
@@ -63,6 +64,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
String CERTS = "certs";
|
||||
String API_APPS = "api_apps";
|
||||
String API_ALLOWED_KEYS = "api_allowed_keys";
|
||||
String OVERRIDDEN_WARNINGS = "overridden_warnings";
|
||||
}
|
||||
|
||||
private static final String CREATE_KEYRINGS_PUBLIC =
|
||||
@@ -172,6 +174,12 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
+ Tables.API_APPS + "(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
private static final String CREATE_OVERRIDDEN_WARNINGS =
|
||||
"CREATE TABLE IF NOT EXISTS " + Tables.OVERRIDDEN_WARNINGS + " ("
|
||||
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ OverriddenWarnings.IDENTIFIER + " TEXT NOT NULL UNIQUE "
|
||||
+ ")";
|
||||
|
||||
public KeychainDatabase(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
mContext = context;
|
||||
@@ -281,10 +289,6 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
case 15:
|
||||
db.execSQL("CREATE INDEX uids_by_name ON user_packets (name COLLATE NOCASE)");
|
||||
db.execSQL("CREATE INDEX uids_by_email ON user_packets (email COLLATE NOCASE)");
|
||||
if (oldVersion == 14) {
|
||||
// no consolidate necessary
|
||||
return;
|
||||
}
|
||||
case 16:
|
||||
// splitUserId changed: Execute consolidate for new parsing of name, email
|
||||
case 17:
|
||||
@@ -294,10 +298,6 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
case 19:
|
||||
// emergency fix for crashing consolidate
|
||||
db.execSQL("UPDATE keys SET is_secure = 1;");
|
||||
if (oldVersion == 18 || oldVersion == 19) {
|
||||
// no consolidate for now, often crashes!
|
||||
return;
|
||||
}
|
||||
/* TODO actually drop this table. leaving it around for now!
|
||||
case 20:
|
||||
db.execSQL("DROP TABLE api_accounts");
|
||||
@@ -306,6 +306,12 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
return;
|
||||
}
|
||||
*/
|
||||
case 20:
|
||||
db.execSQL(CREATE_OVERRIDDEN_WARNINGS);
|
||||
if (oldVersion == 18 || oldVersion == 19 || oldVersion == 20) {
|
||||
// no consolidate for now, often crashes!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: don't depend on consolidate! make migrations inline!
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Vincent Breitmoser <look@my.amazin.horse>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.provider;
|
||||
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.OverriddenWarnings;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
||||
|
||||
|
||||
public class OverriddenWarningsRepository {
|
||||
private final Context context;
|
||||
private KeychainDatabase keychainDatabase;
|
||||
|
||||
public static OverriddenWarningsRepository createOverriddenWarningsRepository(Context context) {
|
||||
return new OverriddenWarningsRepository(context);
|
||||
}
|
||||
|
||||
private OverriddenWarningsRepository(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
private KeychainDatabase getDb() {
|
||||
if (keychainDatabase == null) {
|
||||
keychainDatabase = new KeychainDatabase(context);
|
||||
}
|
||||
return keychainDatabase;
|
||||
}
|
||||
|
||||
public boolean isWarningOverridden(String identifier) {
|
||||
SQLiteDatabase db = getDb().getReadableDatabase();
|
||||
Cursor cursor = db.query(
|
||||
Tables.OVERRIDDEN_WARNINGS,
|
||||
new String[] { "COUNT(*)" },
|
||||
OverriddenWarnings.IDENTIFIER + " = ?",
|
||||
new String[] { identifier },
|
||||
null, null, null);
|
||||
|
||||
try {
|
||||
cursor.moveToFirst();
|
||||
return cursor.getInt(0) > 0;
|
||||
} finally {
|
||||
cursor.close();
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void putOverride(String identifier) {
|
||||
SQLiteDatabase db = getDb().getWritableDatabase();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(OverriddenWarnings.IDENTIFIER, identifier);
|
||||
db.replace(Tables.OVERRIDDEN_WARNINGS, null, cv);
|
||||
db.close();
|
||||
}
|
||||
|
||||
public void deleteOverride(String identifier) {
|
||||
SQLiteDatabase db = getDb().getWritableDatabase();
|
||||
db.delete(Tables.OVERRIDDEN_WARNINGS, OverriddenWarnings.IDENTIFIER + " = ?", new String[] { identifier });
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,14 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RemoteBackupActivity;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RemoteErrorActivity;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RemoteImportKeysActivity;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RemotePassphraseDialogActivity;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RemoteRegisterActivity;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RemoteSecurityProblemDialogActivity;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RemoteSecurityTokenOperationActivity;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RemoteSelectPubKeyActivity;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RequestKeyPermissionActivity;
|
||||
@@ -141,6 +143,22 @@ public class ApiPendingIntentFactory {
|
||||
return createInternal(data, intent);
|
||||
}
|
||||
|
||||
PendingIntent createSecurityProblemIntent(String packageName, DecryptVerifySecurityProblem securityProblem,
|
||||
boolean supportOverride) {
|
||||
Intent intent = new Intent(mContext, RemoteSecurityProblemDialogActivity.class);
|
||||
intent.putExtra(RemoteSecurityProblemDialogActivity.EXTRA_PACKAGE_NAME, packageName);
|
||||
intent.putExtra(RemoteSecurityProblemDialogActivity.EXTRA_SECURITY_PROBLEM, securityProblem);
|
||||
intent.putExtra(RemoteSecurityProblemDialogActivity.EXTRA_SUPPORT_OVERRIDE, supportOverride);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
//noinspection ResourceType, looks like lint is missing FLAG_IMMUTABLE
|
||||
return PendingIntent.getActivity(mContext, 0, intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
private PendingIntent createInternal(Intent data, Intent intent) {
|
||||
// re-attach "data" for pass through. It will be used later to repeat pgp operation
|
||||
intent.putExtra(RemoteSecurityTokenOperationActivity.EXTRA_DATA, data);
|
||||
@@ -175,5 +193,4 @@ public class ApiPendingIntentFactory {
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import org.sufficientlysecure.keychain.operations.results.ExportResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
|
||||
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
|
||||
@@ -60,11 +61,13 @@ import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.OverriddenWarningsRepository;
|
||||
import org.sufficientlysecure.keychain.remote.OpenPgpServiceKeyIdExtractor.KeyIdResult;
|
||||
import org.sufficientlysecure.keychain.remote.OpenPgpServiceKeyIdExtractor.KeyIdResultStatus;
|
||||
import org.sufficientlysecure.keychain.service.BackupKeyringParcel;
|
||||
@@ -400,6 +403,7 @@ public class OpenPgpService extends Service {
|
||||
processDecryptionResultForResultIntent(targetApiVersion, result, pgpResult.getDecryptionResult());
|
||||
processMetadataForResultIntent(result, pgpResult.getDecryptionMetadata());
|
||||
processSignatureResultForResultIntent(targetApiVersion, data, result, pgpResult);
|
||||
processSecurityProblemsPendingIntent(data, result, pgpResult);
|
||||
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
return result;
|
||||
@@ -427,6 +431,29 @@ public class OpenPgpService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
private void processSecurityProblemsPendingIntent(Intent data, Intent result,
|
||||
DecryptVerifyResult decryptVerifyResult) {
|
||||
DecryptVerifySecurityProblem securityProblem = decryptVerifyResult.getSecurityProblem();
|
||||
if (securityProblem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean supportOverride = data.getBooleanExtra(OpenPgpApi.EXTRA_SUPPORT_OVERRIDE_CRYPTO_WARNING, false);
|
||||
if (supportOverride) {
|
||||
SecurityProblem prioritySecurityProblem = securityProblem.getPrioritySecurityProblem();
|
||||
if (prioritySecurityProblem.isIdentifiable()) {
|
||||
String identifier = prioritySecurityProblem.getIdentifier();
|
||||
boolean isOverridden = OverriddenWarningsRepository.createOverriddenWarningsRepository(this)
|
||||
.isWarningOverridden(identifier);
|
||||
result.putExtra(OpenPgpApi.RESULT_OVERRIDE_CRYPTO_WARNING, isOverridden);
|
||||
}
|
||||
}
|
||||
|
||||
String packageName = mApiPermissionHelper.getCurrentCallingPackage();
|
||||
result.putExtra(OpenPgpApi.RESULT_INSECURE_DETAIL_INTENT,
|
||||
mApiPendingIntentFactory.createSecurityProblemIntent(packageName, securityProblem, supportOverride));
|
||||
}
|
||||
|
||||
private void processDecryptionResultForResultIntent(int targetApiVersion, Intent result,
|
||||
OpenPgpDecryptionResult decryptionResult) {
|
||||
if (targetApiVersion < API_VERSION_WITH_DECRYPTION_RESULT) {
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Vincent Breitmoser <look@my.amazin.horse>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.remote.ui;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.remote.ui.SecurityProblemPresenter.RemoteSecurityProblemView;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
|
||||
import org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator;
|
||||
|
||||
|
||||
public class RemoteSecurityProblemDialogActivity extends FragmentActivity {
|
||||
public static final String EXTRA_PACKAGE_NAME = "package_name";
|
||||
public static final String EXTRA_SECURITY_PROBLEM = "security_problem";
|
||||
public static final String EXTRA_SUPPORT_OVERRIDE = "support_override";
|
||||
|
||||
|
||||
private SecurityProblemPresenter presenter;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
this.presenter = new SecurityProblemPresenter(getBaseContext());
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
RemoteRegisterDialogFragment frag = new RemoteRegisterDialogFragment();
|
||||
frag.show(getSupportFragmentManager(), "requestKeyDialog");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
Intent intent = getIntent();
|
||||
String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
|
||||
Serializable keySecurityProblem = intent.getSerializableExtra(EXTRA_SECURITY_PROBLEM);
|
||||
boolean supportOverride = intent.getBooleanExtra(EXTRA_SUPPORT_OVERRIDE, false);
|
||||
|
||||
presenter.setupFromIntentData(packageName, keySecurityProblem, supportOverride);
|
||||
}
|
||||
|
||||
public static class RemoteRegisterDialogFragment extends DialogFragment {
|
||||
public static final int SECONDARY_CHILD_NONE = 0;
|
||||
public static final int SECONDARY_CHILD_RECOMMENDATION = 1;
|
||||
public static final int SECONDARY_CHILD_OVERRIDE = 2;
|
||||
public static final int BUTTON_BAR_REGULAR = 0;
|
||||
public static final int BUTTON_BAR_OVERRIDE = 1;
|
||||
|
||||
private SecurityProblemPresenter presenter;
|
||||
private RemoteSecurityProblemView mvpView;
|
||||
|
||||
private Button buttonGotIt;
|
||||
private Button buttonViewKey;
|
||||
private Button buttonOverride;
|
||||
private Button buttonOverrideUndo;
|
||||
private Button buttonOverrideBack;
|
||||
private Button buttonOverrideConfirm;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Activity activity = getActivity();
|
||||
|
||||
ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity);
|
||||
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(theme);
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
View view = LayoutInflater.from(theme).inflate(R.layout.remote_security_issue_dialog, null, false);
|
||||
alert.setView(view);
|
||||
|
||||
buttonGotIt = (Button) view.findViewById(R.id.button_allow);
|
||||
buttonViewKey = (Button) view.findViewById(R.id.button_view_key);
|
||||
buttonOverride = (Button) view.findViewById(R.id.button_override);
|
||||
buttonOverrideUndo = (Button) view.findViewById(R.id.button_override_undo);
|
||||
buttonOverrideBack = (Button) view.findViewById(R.id.button_override_back);
|
||||
buttonOverrideConfirm = (Button) view.findViewById(R.id.button_override_confirm);
|
||||
|
||||
setupListenersForPresenter();
|
||||
mvpView = createMvpView(view);
|
||||
|
||||
return alert.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
presenter = ((RemoteSecurityProblemDialogActivity) getActivity()).presenter;
|
||||
presenter.setView(mvpView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
super.onCancel(dialog);
|
||||
|
||||
if (presenter != null) {
|
||||
presenter.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
super.onDismiss(dialog);
|
||||
|
||||
if (presenter != null) {
|
||||
presenter.setView(null);
|
||||
presenter = null;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private RemoteSecurityProblemView createMvpView(View view) {
|
||||
final LinearLayout insecureWarningLayout = (LinearLayout) view.findViewById(R.id.insecure_warning_layout);
|
||||
final ImageView iconClientApp = (ImageView) view.findViewById(R.id.icon_client_app);
|
||||
final TextView explanationText = (TextView) insecureWarningLayout.findViewById(R.id.dialog_insecure_text);
|
||||
final TextView recommendText =
|
||||
(TextView) insecureWarningLayout.findViewById(R.id.dialog_insecure_recommend_text);
|
||||
final TextView overrideText =
|
||||
(TextView) insecureWarningLayout.findViewById(R.id.dialog_insecure_override_text);
|
||||
final ToolableViewAnimator secondaryLayoutAnimator =
|
||||
(ToolableViewAnimator) insecureWarningLayout.findViewById(R.id.dialog_insecure_secondary_layout);
|
||||
final ToolableViewAnimator buttonBarAnimator =
|
||||
(ToolableViewAnimator) view.findViewById(R.id.dialog_insecure_button_bar);
|
||||
|
||||
return new RemoteSecurityProblemView() {
|
||||
private boolean layoutInitialized = false;
|
||||
|
||||
@Override
|
||||
public void finishAsCancelled() {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
activity.setResult(RESULT_CANCELED);
|
||||
activity.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishAsSuppressed() {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
activity.setResult(RESULT_OK);
|
||||
activity.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitleClientIcon(Drawable drawable) {
|
||||
iconClientApp.setImageDrawable(drawable);
|
||||
}
|
||||
|
||||
/* specialized layouts, for later?
|
||||
private void inflateWarningContentLayout(int dialog_insecure_mdc) {
|
||||
insecureWarningLayout.removeAllViews();
|
||||
getLayoutInflater(null).inflate(dialog_insecure_mdc, insecureWarningLayout);
|
||||
}
|
||||
*/
|
||||
|
||||
private void showGeneric(String explanationString) {
|
||||
explanationText.setText(explanationString);
|
||||
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_NONE, layoutInitialized);
|
||||
buttonBarAnimator.setDisplayedChild(BUTTON_BAR_REGULAR, layoutInitialized);
|
||||
layoutInitialized = true;
|
||||
}
|
||||
|
||||
private void showGenericWithRecommendation(
|
||||
@StringRes int explanationStringRes, @StringRes int recommendationStringRes) {
|
||||
explanationText.setText(explanationStringRes);
|
||||
recommendText.setText(recommendationStringRes);
|
||||
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_RECOMMENDATION, layoutInitialized);
|
||||
buttonBarAnimator.setDisplayedChild(BUTTON_BAR_REGULAR, layoutInitialized);
|
||||
layoutInitialized = true;
|
||||
}
|
||||
|
||||
private void showGenericWithRecommendation(
|
||||
String explanationString, @StringRes int recommendationStringRes) {
|
||||
explanationText.setText(explanationString);
|
||||
recommendText.setText(recommendationStringRes);
|
||||
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_RECOMMENDATION, layoutInitialized);
|
||||
buttonBarAnimator.setDisplayedChild(BUTTON_BAR_REGULAR, layoutInitialized);
|
||||
layoutInitialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLayoutMissingMdc() {
|
||||
showGenericWithRecommendation(R.string.insecure_mdc, R.string.insecure_mdc_suggestion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLayoutInsecureSymmetric(int symmetricAlgorithm) {
|
||||
showGeneric(getString(R.string.insecure_symmetric_algo,
|
||||
KeyFormattingUtils.getSymmetricCipherName(symmetricAlgorithm)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLayoutInsecureHashAlgorithm(int hashAlgorithm) {
|
||||
showGeneric(getString(R.string.insecure_hash_algo,
|
||||
KeyFormattingUtils.getHashAlgoName(hashAlgorithm)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLayoutEncryptInsecureBitsize(int algorithmId, int bitStrength) {
|
||||
String algorithmName = KeyFormattingUtils.getAlgorithmInfo(algorithmId, null, null);
|
||||
|
||||
showGenericWithRecommendation(
|
||||
getString(R.string.insecure_encrypt_bitstrength, algorithmName),
|
||||
R.string.insecure_encrypt_bitstrength_suggestion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLayoutSignInsecureBitsize(int algorithmId, int bitStrength) {
|
||||
String algorithmName = KeyFormattingUtils.getAlgorithmInfo(algorithmId, null, null);
|
||||
|
||||
showGenericWithRecommendation(
|
||||
getString(R.string.insecure_sign_bitstrength, algorithmName),
|
||||
R.string.insecure_sign_bitstrength_suggestion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLayoutEncryptNotWhitelistedCurve(String curveOid) {
|
||||
showGenericWithRecommendation(
|
||||
getString(R.string.insecure_encrypt_not_whitelisted_curve, curveOid),
|
||||
R.string.insecure_report_suggestion
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLayoutSignNotWhitelistedCurve(String curveOid) {
|
||||
showGenericWithRecommendation(
|
||||
getString(R.string.insecure_sign_not_whitelisted_curve, curveOid),
|
||||
R.string.insecure_report_suggestion
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLayoutEncryptUnidentifiedKeyProblem() {
|
||||
showGenericWithRecommendation(
|
||||
R.string.insecure_encrypt_unidentified,
|
||||
R.string.insecure_report_suggestion
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLayoutSignUnidentifiedKeyProblem() {
|
||||
showGenericWithRecommendation(
|
||||
R.string.insecure_sign_unidentified,
|
||||
R.string.insecure_report_suggestion
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showOverrideMessage(int countdown) {
|
||||
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_OVERRIDE, true);
|
||||
buttonBarAnimator.setDisplayedChild(BUTTON_BAR_OVERRIDE, true);
|
||||
overrideText.setText(getString(R.string.dialog_insecure_override, countdown));
|
||||
buttonOverrideConfirm.setText(
|
||||
getString(R.string.dialog_insecure_button_override_confirm, countdown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showViewKeyButton() {
|
||||
buttonViewKey.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showOverrideButton() {
|
||||
buttonOverride.setVisibility(View.VISIBLE);
|
||||
buttonOverrideUndo.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showOverrideUndoButton() {
|
||||
buttonOverride.setVisibility(View.GONE);
|
||||
buttonOverrideUndo.setVisibility(View.VISIBLE);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void setupListenersForPresenter() {
|
||||
buttonGotIt.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onClickGotIt();
|
||||
}
|
||||
});
|
||||
buttonViewKey.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onClickViewKey();
|
||||
}
|
||||
});
|
||||
buttonOverride.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onClickOverride();
|
||||
}
|
||||
});
|
||||
buttonOverrideUndo.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onClickOverrideUndo();
|
||||
}
|
||||
});
|
||||
buttonOverrideBack.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onClickOverrideBack();
|
||||
}
|
||||
});
|
||||
buttonOverrideConfirm.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onClickOverrideConfirm();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Vincent Breitmoser <look@my.amazin.horse>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.remote.ui;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureBitStrength;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureSigningAlgorithm;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureEncryptionAlgorithm;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.NotWhitelistedCurve;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmProblem;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.UnidentifiedKeyProblem;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.OverriddenWarningsRepository;
|
||||
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
|
||||
|
||||
|
||||
class SecurityProblemPresenter {
|
||||
private static final int OVERRIDE_REQUIRED_COUNT = 3;
|
||||
|
||||
|
||||
private final Context context;
|
||||
private final PackageManager packageManager;
|
||||
private final OverriddenWarningsRepository overriddenWarningsRepository;
|
||||
|
||||
|
||||
private RemoteSecurityProblemView view;
|
||||
private Long viewKeyMasterKeyId;
|
||||
private int overrideCounter;
|
||||
private String securityProblemIdentifier;
|
||||
|
||||
private String packageName;
|
||||
private Serializable securityProblem;
|
||||
private boolean supportOverride;
|
||||
|
||||
|
||||
SecurityProblemPresenter(Context context) {
|
||||
this.context = context;
|
||||
packageManager = context.getPackageManager();
|
||||
overriddenWarningsRepository = OverriddenWarningsRepository.createOverriddenWarningsRepository(context);
|
||||
}
|
||||
|
||||
public void setView(RemoteSecurityProblemView view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
void setupFromIntentData(String packageName, Serializable securityProblem, boolean supportOverride) {
|
||||
this.packageName = packageName;
|
||||
this.securityProblem = securityProblem;
|
||||
this.supportOverride = supportOverride;
|
||||
|
||||
refreshSecurityProblemDisplay();
|
||||
refreshPackageInfo();
|
||||
}
|
||||
|
||||
private void refreshSecurityProblemDisplay() {
|
||||
if (securityProblem instanceof DecryptVerifySecurityProblem) {
|
||||
setupFromDecryptVerifySecurityProblem((DecryptVerifySecurityProblem) securityProblem);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unhandled security problem type!");
|
||||
}
|
||||
}
|
||||
|
||||
private void setupFromDecryptVerifySecurityProblem(DecryptVerifySecurityProblem securityProblem) {
|
||||
if (securityProblem.encryptionKeySecurityProblem != null) {
|
||||
setupFromEncryptionKeySecurityProblem(securityProblem.encryptionKeySecurityProblem);
|
||||
} else if (securityProblem.signingKeySecurityProblem != null) {
|
||||
setupFromSigningKeySecurityProblem(securityProblem.signingKeySecurityProblem);
|
||||
} else if (securityProblem.symmetricSecurityProblem != null) {
|
||||
setupFromEncryptionAlgorithmSecurityProblem(securityProblem.symmetricSecurityProblem);
|
||||
} else if (securityProblem.signatureSecurityProblem != null) {
|
||||
setupFromSignatureSecurityProblem(securityProblem.signatureSecurityProblem);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupFromEncryptionKeySecurityProblem(KeySecurityProblem keySecurityProblem) {
|
||||
viewKeyMasterKeyId = keySecurityProblem.masterKeyId;
|
||||
view.showViewKeyButton();
|
||||
|
||||
if (keySecurityProblem instanceof InsecureBitStrength) {
|
||||
InsecureBitStrength problem = (InsecureBitStrength) keySecurityProblem;
|
||||
view.showLayoutEncryptInsecureBitsize(problem.algorithm, problem.bitStrength);
|
||||
} else if (keySecurityProblem instanceof NotWhitelistedCurve) {
|
||||
NotWhitelistedCurve problem = (NotWhitelistedCurve) keySecurityProblem;
|
||||
view.showLayoutEncryptNotWhitelistedCurve(problem.curveOid);
|
||||
} else if (keySecurityProblem instanceof UnidentifiedKeyProblem) {
|
||||
view.showLayoutEncryptUnidentifiedKeyProblem();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unhandled key security problem type!");
|
||||
}
|
||||
|
||||
if (keySecurityProblem.isIdentifiable()) {
|
||||
securityProblemIdentifier = keySecurityProblem.getIdentifier();
|
||||
refreshOverrideStatusView();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupFromSigningKeySecurityProblem(KeySecurityProblem keySecurityProblem) {
|
||||
viewKeyMasterKeyId = keySecurityProblem.masterKeyId;
|
||||
view.showViewKeyButton();
|
||||
|
||||
if (keySecurityProblem instanceof InsecureBitStrength) {
|
||||
InsecureBitStrength problem = (InsecureBitStrength) keySecurityProblem;
|
||||
view.showLayoutSignInsecureBitsize(problem.algorithm, problem.bitStrength);
|
||||
} else if (keySecurityProblem instanceof NotWhitelistedCurve) {
|
||||
NotWhitelistedCurve problem = (NotWhitelistedCurve) keySecurityProblem;
|
||||
view.showLayoutSignNotWhitelistedCurve(problem.curveOid);
|
||||
} else if (keySecurityProblem instanceof UnidentifiedKeyProblem) {
|
||||
view.showLayoutSignUnidentifiedKeyProblem();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unhandled key security problem type!");
|
||||
}
|
||||
|
||||
if (keySecurityProblem.isIdentifiable()) {
|
||||
securityProblemIdentifier = keySecurityProblem.getIdentifier();
|
||||
refreshOverrideStatusView();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupFromEncryptionAlgorithmSecurityProblem(EncryptionAlgorithmProblem securityProblem) {
|
||||
if (securityProblem instanceof MissingMdc) {
|
||||
view.showLayoutMissingMdc();
|
||||
} else if (securityProblem instanceof InsecureEncryptionAlgorithm) {
|
||||
InsecureEncryptionAlgorithm insecureSymmetricAlgorithm = (InsecureEncryptionAlgorithm) securityProblem;
|
||||
view.showLayoutInsecureSymmetric(insecureSymmetricAlgorithm.symmetricAlgorithm);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unhandled symmetric algorithm problem type!");
|
||||
}
|
||||
|
||||
if (securityProblem.isIdentifiable()) {
|
||||
securityProblemIdentifier = securityProblem.getIdentifier();
|
||||
refreshOverrideStatusView();
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshOverrideStatusView() {
|
||||
if (supportOverride) {
|
||||
if (overriddenWarningsRepository.isWarningOverridden(securityProblemIdentifier)) {
|
||||
view.showOverrideUndoButton();
|
||||
} else {
|
||||
view.showOverrideButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupFromSignatureSecurityProblem(InsecureSigningAlgorithm signatureSecurityProblem) {
|
||||
view.showLayoutInsecureHashAlgorithm(signatureSecurityProblem.hashAlgorithm);
|
||||
}
|
||||
|
||||
private void refreshPackageInfo() {
|
||||
ApplicationInfo applicationInfo;
|
||||
try {
|
||||
applicationInfo = packageManager.getApplicationInfo(packageName, 0);
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new IllegalStateException("Could not retrieve package info!");
|
||||
}
|
||||
Drawable appIcon = packageManager.getApplicationIcon(applicationInfo);
|
||||
// CharSequence appName = packageManager.getApplicationLabel(applicationInfo);
|
||||
|
||||
view.setTitleClientIcon(appIcon);
|
||||
}
|
||||
|
||||
private void incrementOverrideAndDisplayOrTrigger() {
|
||||
int overrideCountLeft = OVERRIDE_REQUIRED_COUNT - overrideCounter;
|
||||
if (overrideCountLeft > 0) {
|
||||
overrideCounter++;
|
||||
view.showOverrideMessage(overrideCountLeft);
|
||||
} else {
|
||||
overriddenWarningsRepository.putOverride(securityProblemIdentifier);
|
||||
view.finishAsSuppressed();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetOverrideStatus() {
|
||||
overrideCounter = 0;
|
||||
overriddenWarningsRepository.deleteOverride(securityProblemIdentifier);
|
||||
}
|
||||
|
||||
void onClickGotIt() {
|
||||
view.finishAsCancelled();
|
||||
}
|
||||
|
||||
void onClickViewKey() {
|
||||
Intent viewKeyIntent = new Intent(context, ViewKeyActivity.class);
|
||||
viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(viewKeyMasterKeyId));
|
||||
context.startActivity(viewKeyIntent);
|
||||
}
|
||||
|
||||
void onClickOverride() {
|
||||
incrementOverrideAndDisplayOrTrigger();
|
||||
}
|
||||
|
||||
void onClickOverrideUndo() {
|
||||
resetOverrideStatus();
|
||||
refreshSecurityProblemDisplay();
|
||||
}
|
||||
|
||||
void onClickOverrideBack() {
|
||||
resetOverrideStatus();
|
||||
refreshSecurityProblemDisplay();
|
||||
}
|
||||
|
||||
void onClickOverrideConfirm() {
|
||||
incrementOverrideAndDisplayOrTrigger();
|
||||
}
|
||||
|
||||
void onCancel() {
|
||||
view.finishAsCancelled();
|
||||
}
|
||||
|
||||
interface RemoteSecurityProblemView {
|
||||
void finishAsCancelled();
|
||||
void finishAsSuppressed();
|
||||
void setTitleClientIcon(Drawable drawable);
|
||||
|
||||
void showLayoutEncryptInsecureBitsize(int algorithmId, int bitStrength);
|
||||
void showLayoutEncryptNotWhitelistedCurve(String curveOid);
|
||||
void showLayoutEncryptUnidentifiedKeyProblem();
|
||||
void showLayoutSignInsecureBitsize(int algorithmId, int bitStrength);
|
||||
void showLayoutSignNotWhitelistedCurve(String curveOid);
|
||||
void showLayoutSignUnidentifiedKeyProblem();
|
||||
|
||||
void showLayoutMissingMdc();
|
||||
void showLayoutInsecureSymmetric(int symmetricAlgorithm);
|
||||
|
||||
void showLayoutInsecureHashAlgorithm(int hashAlgorithm);
|
||||
|
||||
void showOverrideMessage(int countdown);
|
||||
|
||||
void showViewKeyButton();
|
||||
void showOverrideButton();
|
||||
void showOverrideUndoButton();
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,15 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.util;
|
||||
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.DigestException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
@@ -34,8 +43,12 @@ import android.widget.ViewAnimator;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
||||
import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||
import org.bouncycastle.bcpg.HashAlgorithmTags;
|
||||
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
|
||||
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
import org.bouncycastle.crypto.ec.CustomNamedCurves;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.operator.jcajce.PGPUtil;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
@@ -48,14 +61,6 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.DigestException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
public class KeyFormattingUtils {
|
||||
|
||||
public static String getAlgorithmInfo(int algorithm, Integer keySize, String oid) {
|
||||
@@ -226,6 +231,47 @@ public class KeyFormattingUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getHashAlgoName(int hashAlgo) {
|
||||
try {
|
||||
return PGPUtil.getDigestName(hashAlgo);
|
||||
} catch (PGPException e) {
|
||||
return "#" + hashAlgo;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSymmetricCipherName(int algorithm) {
|
||||
switch (algorithm) {
|
||||
case SymmetricKeyAlgorithmTags.TRIPLE_DES:
|
||||
return "Triple-DES";
|
||||
case SymmetricKeyAlgorithmTags.IDEA:
|
||||
return "IDEA";
|
||||
case SymmetricKeyAlgorithmTags.CAST5:
|
||||
return "CAST5";
|
||||
case SymmetricKeyAlgorithmTags.BLOWFISH:
|
||||
return "Blowfish";
|
||||
case SymmetricKeyAlgorithmTags.SAFER:
|
||||
return "SAFER";
|
||||
case SymmetricKeyAlgorithmTags.DES:
|
||||
return "DES";
|
||||
case SymmetricKeyAlgorithmTags.AES_128:
|
||||
return "AES-128";
|
||||
case SymmetricKeyAlgorithmTags.AES_192:
|
||||
return "AES-192";
|
||||
case SymmetricKeyAlgorithmTags.AES_256:
|
||||
return "AES-256";
|
||||
case SymmetricKeyAlgorithmTags.CAMELLIA_128:
|
||||
return "Camellia-128";
|
||||
case SymmetricKeyAlgorithmTags.CAMELLIA_192:
|
||||
return "Camellia-192";
|
||||
case SymmetricKeyAlgorithmTags.CAMELLIA_256:
|
||||
return "Camellia-256";
|
||||
case SymmetricKeyAlgorithmTags.TWOFISH:
|
||||
return "Twofish";
|
||||
default:
|
||||
return "#" + algorithm;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts fingerprint to hex
|
||||
* <p/>
|
||||
|
||||
@@ -56,6 +56,7 @@ public class FoldableLinearLayout extends LinearLayout {
|
||||
|
||||
private String mFoldedLabel;
|
||||
private String mUnFoldedLabel;
|
||||
private boolean mInitializeFolded;
|
||||
|
||||
public FoldableLinearLayout(Context context) {
|
||||
super(context);
|
||||
@@ -84,6 +85,7 @@ public class FoldableLinearLayout extends LinearLayout {
|
||||
R.styleable.FoldableLinearLayout, 0, 0);
|
||||
mFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_foldedLabel);
|
||||
mUnFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_unFoldedLabel);
|
||||
mInitializeFolded = a.getBoolean(R.styleable.FoldableLinearLayout_initializeFolded, true);
|
||||
a.recycle();
|
||||
}
|
||||
// If any attribute isn't found then set a default one
|
||||
@@ -102,6 +104,10 @@ public class FoldableLinearLayout extends LinearLayout {
|
||||
|
||||
initialiseInnerViews();
|
||||
|
||||
if (!mInitializeFolded) {
|
||||
toggleFolded();
|
||||
}
|
||||
|
||||
super.onFinishInflate();
|
||||
}
|
||||
|
||||
@@ -149,42 +155,46 @@ public class FoldableLinearLayout extends LinearLayout {
|
||||
foldableControl.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mFolded = !mFolded;
|
||||
if (mFolded) {
|
||||
mFoldableIcon.setImageResource(R.drawable.ic_expand_less_black_24dp);
|
||||
mFoldableContainer.setVisibility(View.VISIBLE);
|
||||
AlphaAnimation animation = new AlphaAnimation(0f, 1f);
|
||||
animation.setDuration(mShortAnimationDuration);
|
||||
mFoldableContainer.startAnimation(animation);
|
||||
mFoldableTextView.setText(mUnFoldedLabel);
|
||||
|
||||
} else {
|
||||
mFoldableIcon.setImageResource(R.drawable.ic_expand_more_black_24dp);
|
||||
AlphaAnimation animation = new AlphaAnimation(1f, 0f);
|
||||
animation.setDuration(mShortAnimationDuration);
|
||||
animation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
// making sure that at the end the container is completely removed from view
|
||||
mFoldableContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
});
|
||||
mFoldableContainer.startAnimation(animation);
|
||||
mFoldableTextView.setText(mFoldedLabel);
|
||||
}
|
||||
toggleFolded();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void toggleFolded() {
|
||||
mFolded = !mFolded;
|
||||
if (mFolded) {
|
||||
mFoldableIcon.setImageResource(R.drawable.ic_expand_less_black_24dp);
|
||||
mFoldableContainer.setVisibility(View.VISIBLE);
|
||||
AlphaAnimation animation = new AlphaAnimation(0f, 1f);
|
||||
animation.setDuration(mShortAnimationDuration);
|
||||
mFoldableContainer.startAnimation(animation);
|
||||
mFoldableTextView.setText(mUnFoldedLabel);
|
||||
|
||||
} else {
|
||||
mFoldableIcon.setImageResource(R.drawable.ic_expand_more_black_24dp);
|
||||
AlphaAnimation animation = new AlphaAnimation(1f, 0f);
|
||||
animation.setDuration(mShortAnimationDuration);
|
||||
animation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
// making sure that at the end the container is completely removed from view
|
||||
mFoldableContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
});
|
||||
mFoldableContainer.startAnimation(animation);
|
||||
mFoldableTextView.setText(mFoldedLabel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds provided child view to foldableContainer View
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user