Merge pull request #2097 from open-keychain/security-problems

Security Warnings API
This commit is contained in:
Dominik Schürmann
2017-05-17 13:52:56 +03:00
committed by GitHub
35 changed files with 1632 additions and 313 deletions

View File

@@ -859,6 +859,11 @@
android:exported="false"
android:theme="@style/Theme.Keychain.Transparent"
android:label="@string/app_name" />
<activity
android:name=".remote.ui.RemoteSecurityProblemDialogActivity"
android:exported="false"
android:theme="@style/Theme.Keychain.Transparent"
android:label="@string/app_name" />
<activity
android:name=".remote.ui.RemoteSelectPubKeyActivity"
android:exported="false"

View File

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

View File

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

View File

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

View File

@@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 were 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
*/

View File

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

View File

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

View File

@@ -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!

View File

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

View File

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

View File

@@ -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) {

View File

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

View File

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

View File

@@ -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/>

View File

@@ -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
*

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="@layout/remote_security_issue_dialog">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:textAppearanceLarge"
android:text="Security Warning"
android:id="@+id/dialog_title"
/>
<TextView
android:id="@+id/api_register_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="The key this message was sent by is using an outdated algorithm, and is no longer considered secure!"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="The algorithm in use is DSA 1024 bit, which has been considered insecure since 2010."
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="?android:textAppearanceMedium"
android:text="Recommended Action"
android:id="@+id/dialog_title_2"
/>
<TextView
android:id="@+id/api_register_text_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="The key in use is insecure, and cannot be updated. To communicate securely, the sender must create a new key!"
/>
</merge>

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
tools:showIn="@layout/remote_security_issue_dialog">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:textAppearanceLarge"
android:text="@string/dialog_insecure_title"
android:id="@+id/dialog_title"
/>
<TextView
android:id="@+id/dialog_insecure_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
tools:text="The key this message was sent by is using an outdated algorithm, and is no longer considered secure!\n\nThe algorithm in use is DSA 1024 bit, which has been considered insecure since 2010."
/>
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/dialog_insecure_secondary_layout"
android:inAnimation="@anim/fade_in"
android:outAnimation="@anim/fade_out"
android:measureAllChildren="true"
custom:initialView="1">
<android.support.v4.widget.Space
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="?android:textAppearanceMedium"
android:text="@string/dialog_insecure_recommend_title"
android:id="@+id/dialog_title_2"
/>
<TextView
android:id="@+id/dialog_insecure_recommend_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
tools:text="The key in use is insecure, and cannot be updated. To communicate securely, the sender must create a new key!"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="@color/android_red_light"
android:text="@string/dialog_insecure_override_title"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:id="@+id/dialog_insecure_override_text"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
tools:text="@string/dialog_insecure_override"
/>
</LinearLayout>
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
</merge>

View File

@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:layout_marginTop="24dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:elevation="4dp"
android:background="?attr/colorPrimary"
android:gravity="center_horizontal"
tools:targetApi="lollipop">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:src="@drawable/link_24dp"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/icon_client_app"
tools:src="@drawable/apps_k9"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<!-- The parent properly determines the height of this scroll view, it is *not* useless -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="ifContentScrolls"
tools:ignore="UselessParent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingTop="24dp"
android:paddingBottom="16dp"
android:id="@+id/insecure_warning_layout"
>
<include layout="@layout/dialog_insecure_generic" />
</LinearLayout>
</ScrollView>
</LinearLayout>
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/dialog_insecure_button_bar"
android:inAnimation="@anim/fade_in"
android:outAnimation="@anim/fade_out"
tools:layout_marginBottom="50dp"
custom:initialView="0">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end"
android:padding="8dp"
style="?buttonBarStyle">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_insecure_button_override"
android:id="@+id/button_override"
android:visibility="gone"
tools:visibility="visible"
style="?buttonBarButtonStyle" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_insecure_button_undo"
android:id="@+id/button_override_undo"
android:visibility="gone"
style="?buttonBarButtonStyle" />
<android.support.v4.widget.Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_insecure_button_view_key"
android:id="@+id/button_view_key"
android:visibility="gone"
tools:visibility="visible"
style="?buttonBarButtonStyle" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_insecure_button_ok"
android:id="@+id/button_allow"
style="?buttonBarButtonStyle" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end"
android:padding="8dp"
style="?buttonBarStyle">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Back"
android:id="@+id/button_override_back"
style="?buttonBarButtonStyle" />
<android.support.v4.widget.Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_insecure_button_override_confirm"
android:id="@+id/button_override_confirm"
style="?buttonBarButtonStyle" />
</LinearLayout>
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
</LinearLayout>

View File

@@ -8,6 +8,7 @@
<declare-styleable name="FoldableLinearLayout">
<attr name="foldedLabel" format="string" />
<attr name="unFoldedLabel" format="string" />
<attr name="initializeFolded" format="boolean" />
</declare-styleable>
<!-- Taken from Matt Allen Password Strength View

View File

@@ -1853,4 +1853,31 @@
<string name="key_expiry_text">"This key expired on <b>%1$s</b>."</string>
<string name="dialog_insecure_title">Security Warning</string>
<string name="dialog_insecure_recommend_title">Recommended Action</string>
<string name="insecure_encrypt_bitstrength">"The key you used to receive (decrypt) this message is outdated!\n\nThe algorithm (%s) is configured with a strength of 1024 bits or less, which has been considered insecure for general use since 2006."</string>
<string name="insecure_encrypt_bitstrength_suggestion">The key in use is insecure, and cannot be updated. To communicate securely, you must create a new key!</string>
<string name="insecure_sign_bitstrength">"The key that sent (signed) this message is outdated!\n\nThe algorithm (%s) is configured with a strength of 1024 bits or less, which has been considered insecure for general use since 2006."</string>
<string name="insecure_sign_bitstrength_suggestion">The key in use is insecure, and cannot be updated. To communicate securely, the sender must create a new key!</string>
<string name="insecure_encrypt_not_whitelisted_curve">"The key that received (decrypted) this message is using the elliptic curve with OID %1$s, which is not whitelisted!"</string>
<string name="insecure_sign_not_whitelisted_curve">"The key this message was sent (signed) from is using the elliptic curve with OID %1$s, which is not whitelisted!"</string>
<string name="insecure_encrypt_unidentified">"There is an unidentified security problem with the key used to receive (decrypyt) this message!"</string>
<string name="insecure_sign_unidentified">"There is an unidentified security problem with the key used to send (sign) this message!"</string>
<string name="insecure_report_suggestion">"This might be a problem in OpenKeychain, please report on our issue tracker!"</string>
<string name="insecure_mdc">"This message was not signed, and did also not contain a Modification Detection Code (MDC). It may have been modified by an attacker!"</string>
<string name="insecure_mdc_suggestion">"A missing MDC is a problem in the sending software, or an attack. For secure end-to-end communication, messages should also be signed by the sender!"</string>
<string name="insecure_symmetric_algo">"This message was encrypted using the symmetric %s algorithm. This is considered insecure, or at least exotic!"</string>
<string name="insecure_hash_algo">"This message was signed using the %s hashing algorithm. This is considered insecure, or at least exotic!"</string>
<string name="dialog_insecure_override">If you don\'t want to be warned about this specific security problem in the future, you can suppress this warning.</string>
<string name="dialog_insecure_override_title">Suppress this warning</string>
<string name="dialog_insecure_override_ok_title">Warning suppressed</string>
<string name="dialog_insecure_override_ok">The security warning (for this key/message) will not be shown again in the future.</string>
<string name="dialog_insecure_button_override">Suppress</string>
<string name="dialog_insecure_button_override_confirm">Suppress (%d)</string>
<string name="dialog_insecure_button_undo">Un-Suppress</string>
<string name="dialog_insecure_button_view_key">View Key</string>
<string name="dialog_insecure_button_ok">Got it</string>
</resources>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2014-2017 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,11 +18,23 @@
package org.sufficientlysecure.keychain.pgp;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.security.Security;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import org.apache.tools.ant.util.StringUtils;
import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.Packet;
import org.bouncycastle.bcpg.PacketTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Assert;
@@ -38,13 +50,13 @@ import org.robolectric.shadows.ShadowLog;
import org.sufficientlysecure.keychain.KeychainTestRunner;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureBitStrength;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureEncryptionAlgorithm;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
@@ -55,28 +67,18 @@ import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.Security;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import static org.hamcrest.core.AnyOf.anyOf;
import static org.hamcrest.core.Is.is;
@SuppressWarnings("WeakerAccess")
@RunWith(KeychainTestRunner.class)
public class PgpEncryptDecryptTest {
static Passphrase mSymmetricPassphrase = TestingUtils.genPassphrase(true);
static UncachedKeyRing mStaticRing1, mStaticRing2, mStaticRingInsecure;
static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true);
// static Passphrase mKeyPhraseInsecure = TestingUtils.genPassphrase(true);
static Passphrase mKeyPhrase1, mKeyPhrase2;
static PrintStream oldShadowStream;
@@ -86,65 +88,64 @@ public class PgpEncryptDecryptTest {
oldShadowStream = ShadowLog.stream;
// ShadowLog.stream = System.out;
PgpKeyOperation op = new PgpKeyOperation(null);
/* generation parameters:
{
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("bloom");
parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase1));
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("bloom");
parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase("RsKrW^raOPcnQ=ZJr-pP")));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing());
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
*/
mStaticRing1 = result.getRing();
}
mKeyPhrase1 = new Passphrase("RsKrW^raOPcnQ=ZJr-pP");
mStaticRing1 = KeyringTestingHelper.readRingFromResource("/test-keys/encrypt_decrypt_key_1.sec");
{
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("belle");
parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase2));
/* generation:
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("belle");
parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase("x")));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing());
PgpKeyOperation op = new PgpKeyOperation(new ProgressScaler());
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
new FileOutputStream("/tmp/key.sec").write(result.getRing().getEncoded());
*/
mStaticRing2 = result.getRing();
}
mKeyPhrase2 = new Passphrase("x");
mStaticRing2 = KeyringTestingHelper.readRingFromResource("/test-keys/encrypt_decrypt_key_2.sec");
// {
// // insecure (1024 bit) RSA key
// SaveKeyringParcel parcel = new SaveKeyringParcel();
// parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
// Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
// parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
// Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
// parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
// Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
// parcel.mAddUserIds.add("eve");
// parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhraseInsecure);
//
// PgpEditKeyResult result = op.createSecretKeyRing(parcel);
// Assert.assertTrue("initial test key creation must succeed", result.success());
// Assert.assertNotNull("initial test key creation must succeed", result.getRing());
//
// mStaticRingInsecure = result.getRing();
// }
/* generation parameters (insecure key, requires removal of of security checks!):
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("eve");
parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase("")));
PgpEditKeyResult result = new PgpKeyOperation(new ProgressScaler()).createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing());
new FileOutputStream("/tmp/key.sec").write(result.getRing().getEncoded());
*/
mStaticRingInsecure = KeyringTestingHelper.readRingFromResource("/test-keys/encrypt_decrypt_key_insecure.sec");
}
@Before
@@ -157,6 +158,7 @@ public class PgpEncryptDecryptTest {
databaseInteractor.saveSecretKeyRing(mStaticRing1, new ProgressScaler());
databaseInteractor.saveSecretKeyRing(mStaticRing2, new ProgressScaler());
databaseInteractor.saveSecretKeyRing(mStaticRingInsecure, new ProgressScaler());
// ok NOW log verbosely!
ShadowLog.stream = System.out;
@@ -420,7 +422,7 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData();
PgpSignEncryptData pgpData = new PgpSignEncryptData();
// only sign, as cleartext
pgpData.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId());
pgpData.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
@@ -997,76 +999,73 @@ public class PgpEncryptDecryptTest {
}
@Test
public void testAsymmetricInsecureEncryptDecrypt() {
public void testAsymmetricSymmetricDesDecrypt() throws Exception {
InputStream in = getResourceAsStream("/test-ciphertexts/algo_des.pgp.asc");
String plaintext = "dies ist ein plaintext ☭";
// insecure symmetric algo
subtestInsecureEncryptDecrypt(mStaticRing1, mKeyPhrase1,
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.DES, true);
// don't use MDC
subtestInsecureEncryptDecrypt(mStaticRing1, mKeyPhrase1,
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_256, false);
// TODO: test not working!
// insecure key (1024 bit RSA)
// subtestInsecureEncryptDecrypt(mStaticRingInsecure, mKeyPhraseInsecure,
// PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_256, true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out);
Assert.assertTrue(result.success());
Assert.assertArrayEquals(out.toByteArray(), plaintext.getBytes());
Assert.assertEquals(OpenPgpDecryptionResult.RESULT_INSECURE, result.getDecryptionResult().getResult());
Assert.assertEquals(OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
InsecureEncryptionAlgorithm symmetricSecurityProblem =
(InsecureEncryptionAlgorithm) result.getSecurityProblem().symmetricSecurityProblem;
Assert.assertEquals((symmetricSecurityProblem).symmetricAlgorithm, SymmetricKeyAlgorithmTags.DES);
}
private void subtestInsecureEncryptDecrypt(UncachedKeyRing key, Passphrase passphrase,
int algorithm, boolean isIntegrityProtected) {
String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
byte[] ciphertext;
public void testAsymmetricNoMdcDecrypt() throws Exception {
InputStream in = getResourceAsStream("/test-ciphertexts/no_mdc.pgp.asc");
String plaintext = "dies ist ein plaintext ☭";
{ // encrypt data with insecure key
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputData data = new InputData(in, in.available());
InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out);
PgpSignEncryptData pgpData = new PgpSignEncryptData();
pgpData.setEncryptionMasterKeyIds(new long[]{key.getMasterKeyId()})
.setSymmetricEncryptionAlgorithm(algorithm)
.setIntegrityProtected(isIntegrityProtected);
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData);
Assert.assertTrue(result.success());
Assert.assertArrayEquals(out.toByteArray(), plaintext.getBytes());
Assert.assertEquals(OpenPgpDecryptionResult.RESULT_INSECURE, result.getDecryptionResult().getResult());
Assert.assertEquals(OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
data, out);
Assert.assertTrue("encryption must succeed", result.success());
Assert.assertTrue(result.getSecurityProblem().symmetricSecurityProblem instanceof MissingMdc);
}
ciphertext = out.toByteArray();
}
public void testAsymmetricRsa1024Decrypt() throws Exception {
InputStream in = getResourceAsStream("/test-ciphertexts/rsa_1024.pgp.asc");
String plaintext = "dies ist ein plaintext ☭";
{ // decryption with provided passphrase should yield insecure status
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
InputData data = new InputData(in, in.available());
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(passphrase), data, out);
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
Assert.assertArrayEquals("decrypted ciphertext with provided passphrase should equal plaintext",
out.toByteArray(), plaintext.getBytes());
Assert.assertEquals("decryptionResult should be RESULT_INSECURE",
OpenPgpDecryptionResult.RESULT_INSECURE, result.getDecryptionResult().getResult());
Assert.assertEquals("signatureResult should be RESULT_NO_SIGNATURE",
OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel();
Assert.assertEquals("must have one cached session key",
1, cryptoInput.getCryptoData().size());
Assert.assertTrue(result.success());
Assert.assertArrayEquals(out.toByteArray(), plaintext.getBytes());
Assert.assertEquals(OpenPgpDecryptionResult.RESULT_INSECURE, result.getDecryptionResult().getResult());
Assert.assertEquals(OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
OpenPgpMetadata metadata = result.getDecryptionMetadata();
Assert.assertEquals("filesize must be correct",
out.toByteArray().length, metadata.getOriginalSize());
}
InsecureBitStrength encryptionKeySecurityProblem =
(InsecureBitStrength) result.getSecurityProblem().encryptionKeySecurityProblem;
Assert.assertEquals(mStaticRingInsecure.getMasterKeyId(), encryptionKeySecurityProblem.masterKeyId);
Assert.assertEquals(PublicKeyAlgorithmTags.RSA_ENCRYPT, encryptionKeySecurityProblem.algorithm);
Assert.assertEquals(1024, encryptionKeySecurityProblem.bitStrength);
}
private PgpDecryptVerifyOperation operationWithFakePassphraseCache(
@@ -1092,4 +1091,34 @@ public class PgpEncryptDecryptTest {
}
};
}
private static InputStream getResourceAsStream(String name) {
return PgpEncryptDecryptTest.class.getResourceAsStream(name);
}
/* skeleton for generating test data
@Test
public void generateData() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream("dies ist ein plaintext ☭".getBytes());
PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData();
pgpData.setEncryptionMasterKeyIds(new long[]{ mStaticRingInsecure.getMasterKeyId()});
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
data, out);
Assert.assertTrue("encryption must succeed", result.success());
ArmoredOutputStream armoredOutputStream = new ArmoredOutputStream(new FileOutputStream("/tmp/rsa_1024.pgp.asc"));
armoredOutputStream.write(out.toByteArray());
armoredOutputStream.close();
}
*/
}

View File

@@ -152,7 +152,7 @@ public class UncachedKeyringTest {
readRingFromResource("/test-keys/broken_cert_version.asc");
}
UncachedKeyRing readRingFromResource(String name) throws Throwable {
private UncachedKeyRing readRingFromResource(String name) throws Throwable {
return UncachedKeyRing.fromStream(UncachedKeyringTest.class.getResourceAsStream(name)).next();
}

View File

@@ -364,4 +364,7 @@ public class KeyringTestingHelper {
return result;
}
public static UncachedKeyRing readRingFromResource(String name) throws Exception {
return UncachedKeyRing.fromStream(KeyringTestingHelper.class.getResourceAsStream(name)).next();
}
}

View File

@@ -0,0 +1,13 @@
-----BEGIN PGP MESSAGE-----
Testcase: encrypted using DES (should be marked insecure)
Plaintext: 'dies ist ein plaintext ☭'
hH4DGQB0mNo0qa0SAgME9glrSS/q7JAz+wh0WoOydUAVsCS9jaMRx8gBWrx8NJB4
OcEZnYiKU5Nu14qDkPKpqUwq0l/D2ykvfk3oDerb2jDL2M9j+sy/lWPeaSd7MbWO
8cVKqMaNkXFSDp4V/wRTrj+xnDaZZ2v/OUT3jWZuneGEfgNG/bBLBjduwxICAwQS
Jr1sGq9DUNHkhDXX+t9Y7Z1idE7kr/skjziBP3L1RhIavVtU6sEjDOs/3IAZUgQx
RnU7ITIw0dVrVMPCZ0zWMEG5WZCBCtqdPHfXyESTqR891e5uqAtLf9Og7LIPkwiN
GXNc3r+dm5tcqfALmmwHttJDAYW22pFTOf52358ENWEBIgePFiKXImAXjS/VAVAV
t1OnpO81+BnJDBTrbzbXYy/2wxFMwnqt6HiVLTSTB93p61VFAw==
=4WKW
-----END PGP MESSAGE-----

View File

@@ -0,0 +1,13 @@
-----BEGIN PGP MESSAGE-----
Testcase: encrypted and unsigned, without mdc
Plaintext: 'dies ist ein plaintext ☭'
hH4DGQB0mNo0qa0SAgMEThtzE4S/Y1z03qmQ05p0uydye5zzgFSyefNFmvneu1rh
m7VX0bXfQfnK3QlKtONFzhgzmZLIdo8q6JNP1CtkfTCOjRgcA6sdZA6PbPgAXgoq
CY1Dpt69K+TOzD37jxpzbEyB2S3yEO9VjxJVo6u0JbSEfgNG/bBLBjduwxICAwSU
sILMkNnz3Qz9kSrjNwXR/PcQSdOdt9EI4ei26+Fp+eC6txTo8DC7YMcdXe9XqXoe
xX5DvjhoBOQvCjmeCbeXMDxflR/ygybvZCyzrVxgTs7kkjUalI16saXjkelcF3Kt
WVnLRbVs0+SWMwLt+Nf6HMk0ieELGF6MMR+2AXNC0VY3b3WwJAMWZFsz9P4VG7rP
YiHtyfyVSnXiyaKPzevD3TKaZtse/g==
=sUCU
-----END PGP MESSAGE-----

View File

@@ -0,0 +1,11 @@
-----BEGIN PGP MESSAGE-----
Testcase: 1024 bit rsa (should be marked insecure)
Plaintext: 'dies ist ein plaintext ☭'
hIsDUIIrlXQHiJEBA/YiKQH9teIvmGQ057tyUsVplsM68phoS6xG9aMP1DXZsHB3
a0nzPCJ/s/9Ct9X1VwrFv6gKMZVAjDOvizDpc8Q+R88BMyXxcxTJjg7x4ZIGxLAL
7p7aEBJj243SWgu9SVQ5I6zF4M0RZBb50Q5EVIjeYhI2HN3CZYQTllgZySgg0ksB
rHEsemlxXivKscIkhYSAlkx9pGQ+eEIXtgzsFacbYuk2rVAztaO7NQFu/Zrnbpyn
UMdSEVfpIBLLHep71wA4P55fxBubAJ/YUvw=
=v0D8
-----END PGP MESSAGE-----