aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java91
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java114
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java105
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java36
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSecurityConstants.java262
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java9
10 files changed, 498 insertions, 259 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index 31a3925da..7394c07c3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -43,7 +43,6 @@ import org.sufficientlysecure.keychain.util.Passphrase;
import java.nio.ByteBuffer;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateCrtKey;
-import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -177,16 +176,6 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
return true;
}
- /**
- * Returns a list of all supported hash algorithms.
- */
- public ArrayList<Integer> getSupportedHashAlgorithms() {
- // TODO: intersection between preferred hash algos of this key and PgpConstants.PREFERRED_HASH_ALGORITHMS
- // choose best algo
-
- return PgpConstants.sPreferredHashAlgorithms;
- }
-
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo,
Map<ByteBuffer,byte[]> signedHashes) {
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
@@ -205,7 +194,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
public PGPSignatureGenerator getCertSignatureGenerator(Map<ByteBuffer, byte[]> signedHashes) {
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
- PgpConstants.CERTIFY_HASH_ALGO, signedHashes);
+ PgpSecurityConstants.CERTIFY_HASH_ALGO, signedHashes);
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java
new file mode 100644
index 000000000..c4525e5cd
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.pgp;
+
+import org.openintents.openpgp.OpenPgpDecryptionResult;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class OpenPgpDecryptionResultBuilder {
+
+ // builder
+ private boolean mInsecure = false;
+ private boolean mEncrypted = false;
+
+ public void setInsecure(boolean insecure) {
+ this.mInsecure = insecure;
+ }
+
+ public void setEncrypted(boolean encrypted) {
+ this.mEncrypted = encrypted;
+ }
+
+ public OpenPgpDecryptionResult build() {
+ OpenPgpDecryptionResult result = new OpenPgpDecryptionResult();
+
+ if (mInsecure) {
+ Log.d(Constants.TAG, "RESULT_INSECURE");
+ result.setResult(OpenPgpDecryptionResult.RESULT_INSECURE);
+ return result;
+ }
+
+ if (mEncrypted) {
+ Log.d(Constants.TAG, "RESULT_ENCRYPTED");
+ result.setResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED);
+ } else {
+ Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED");
+ result.setResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED);
+ }
+
+ return result;
+ }
+
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
index ed4715681..9d059b58f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
@@ -30,7 +30,6 @@ import java.util.ArrayList;
*/
public class OpenPgpSignatureResultBuilder {
// OpenPgpSignatureResult
- private boolean mSignatureOnly = false;
private String mPrimaryUserId;
private ArrayList<String> mUserIds = new ArrayList<>();
private long mKeyId;
@@ -42,10 +41,7 @@ public class OpenPgpSignatureResultBuilder {
private boolean mIsSignatureKeyCertified = false;
private boolean mIsKeyRevoked = false;
private boolean mIsKeyExpired = false;
-
- public void setSignatureOnly(boolean signatureOnly) {
- this.mSignatureOnly = signatureOnly;
- }
+ private boolean mInsecure = false;
public void setPrimaryUserId(String userId) {
this.mPrimaryUserId = userId;
@@ -63,6 +59,10 @@ public class OpenPgpSignatureResultBuilder {
this.mValidSignature = validSignature;
}
+ public void setInsecure(boolean insecure) {
+ this.mInsecure = insecure;
+ }
+
public void setSignatureKeyCertified(boolean isSignatureKeyCertified) {
this.mIsSignatureKeyCertified = isSignatureKeyCertified;
}
@@ -87,6 +87,10 @@ public class OpenPgpSignatureResultBuilder {
return mValidSignature;
}
+ public boolean isInsecure() {
+ return mInsecure;
+ }
+
public void initValid(CanonicalizedPublicKeyRing signingRing,
CanonicalizedPublicKey signingKey) {
setSignatureAvailable(true);
@@ -109,47 +113,50 @@ public class OpenPgpSignatureResultBuilder {
}
public OpenPgpSignatureResult build() {
- if (mSignatureAvailable) {
- OpenPgpSignatureResult result = new OpenPgpSignatureResult();
- result.setSignatureOnly(mSignatureOnly);
-
- // valid sig!
- if (mKnownKey) {
- if (mValidSignature) {
- result.setKeyId(mKeyId);
- result.setPrimaryUserId(mPrimaryUserId);
- result.setUserIds(mUserIds);
-
- if (mIsKeyRevoked) {
- Log.d(Constants.TAG, "SIGNATURE_KEY_REVOKED");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED);
- } else if (mIsKeyExpired) {
- Log.d(Constants.TAG, "SIGNATURE_KEY_EXPIRED");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED);
- } else if (mIsSignatureKeyCertified) {
- Log.d(Constants.TAG, "SIGNATURE_SUCCESS_CERTIFIED");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED);
- } else {
- Log.d(Constants.TAG, "SIGNATURE_SUCCESS_UNCERTIFIED");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED);
- }
- } else {
- Log.d(Constants.TAG, "Error! Invalid signature.");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
- }
- } else {
- result.setKeyId(mKeyId);
-
- Log.d(Constants.TAG, "SIGNATURE_KEY_MISSING");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_KEY_MISSING);
- }
+ OpenPgpSignatureResult result = new OpenPgpSignatureResult();
+ if (!mSignatureAvailable) {
+ Log.d(Constants.TAG, "RESULT_NO_SIGNATURE");
+ result.setResult(OpenPgpSignatureResult.RESULT_NO_SIGNATURE);
return result;
- } else {
- Log.d(Constants.TAG, "no signature found!");
+ }
+
+ if (!mKnownKey) {
+ result.setKeyId(mKeyId);
- return null;
+ Log.d(Constants.TAG, "RESULT_KEY_MISSING");
+ result.setResult(OpenPgpSignatureResult.RESULT_KEY_MISSING);
+ return result;
+ }
+
+ if (!mValidSignature) {
+ Log.d(Constants.TAG, "RESULT_INVALID_SIGNATURE");
+ result.setResult(OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE);
+ return result;
}
+
+ result.setKeyId(mKeyId);
+ result.setPrimaryUserId(mPrimaryUserId);
+ result.setUserIds(mUserIds);
+
+ if (mIsKeyRevoked) {
+ Log.d(Constants.TAG, "RESULT_INVALID_KEY_REVOKED");
+ result.setResult(OpenPgpSignatureResult.RESULT_INVALID_KEY_REVOKED);
+ } else if (mIsKeyExpired) {
+ Log.d(Constants.TAG, "RESULT_INVALID_KEY_EXPIRED");
+ result.setResult(OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED);
+ } else if (mInsecure) {
+ Log.d(Constants.TAG, "RESULT_INVALID_INSECURE");
+ result.setResult(OpenPgpSignatureResult.RESULT_INVALID_INSECURE);
+ } else if (mIsSignatureKeyCertified) {
+ Log.d(Constants.TAG, "RESULT_VALID_CONFIRMED");
+ result.setResult(OpenPgpSignatureResult.RESULT_VALID_CONFIRMED);
+ } else {
+ Log.d(Constants.TAG, "RESULT_VALID_UNCONFIRMED");
+ result.setResult(OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED);
+ }
+
+ return result;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java
deleted file mode 100644
index f739b1e6d..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.pgp;
-
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
-import org.spongycastle.bcpg.HashAlgorithmTags;
-import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
-
-import java.util.ArrayList;
-
-public class PgpConstants {
-
- public static ArrayList<Integer> sPreferredSymmetricAlgorithms = new ArrayList<>();
- public static ArrayList<Integer> sPreferredHashAlgorithms = new ArrayList<>();
- public static ArrayList<Integer> sPreferredCompressionAlgorithms = new ArrayList<>();
-
- // TODO: use hashmaps for contains in O(1) and intersections!
-
- /*
- * Most preferred is first
- * These arrays are written as preferred algorithms into the keys on creation.
- * Other implementations may choose to honor this selection.
- *
- * These lists also define the only algorithms which are used in OpenKeychain.
- * We do not support algorithms such as MD5
- */
- static {
- sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_256);
- sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_192);
- sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_128);
- sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.TWOFISH);
-
- // NOTE: some implementations do not support SHA512, thus we choose SHA256 as default (Mailvelope?)
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA256);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA512);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA384);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA224);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA1);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.RIPEMD160);
-
- /*
- * Prefer ZIP
- * "ZLIB provides no benefit over ZIP and is more malleable"
- * - (OpenPGP WG mailinglist: "[openpgp] Intent to deprecate: Insecure primitives")
- * BZIP2: very slow
- */
- sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZIP);
- sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZLIB);
- sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.BZIP2);
- }
-
- public static final int CERTIFY_HASH_ALGO = HashAlgorithmTags.SHA256;
-
- /*
- * Note: s2kcount is a number between 0 and 0xff that controls the
- * number of times to iterate the password hash before use. More
- * iterations are useful against offline attacks, as it takes more
- * time to check each password. The actual number of iterations is
- * rather complex, and also depends on the hash function in use.
- * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
- * you more iterations. As a rough rule of thumb, when using
- * SHA256 as the hashing function, 0x10 gives you about 64
- * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
- * or about 1 million iterations. The maximum you can go to is
- * 0xff, or about 2 million iterations.
- * from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
- *
- * Bouncy Castle default: 0x60
- * kbsriram proposes: 0xc0
- * OpenKeychain: 0x90
- */
- public static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90;
- public static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA256;
- public static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
- public static final int SECRET_KEY_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA256;
- // NOTE: only SHA1 is supported for key checksum calculations in OpenPGP,
- // see http://tools.ietf.org/html/rfc488 0#section-5.5.3
- public static final int SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO = HashAlgorithmTags.SHA1;
-
- public static interface OpenKeychainSymmetricKeyAlgorithmTags extends SymmetricKeyAlgorithmTags {
- public static final int USE_PREFERRED = -1;
- }
-
- public static interface OpenKeychainHashAlgorithmTags extends HashAlgorithmTags {
- public static final int USE_PREFERRED = -1;
- }
-
- public static interface OpenKeychainCompressionAlgorithmTags extends CompressionAlgorithmTags {
- public static final int USE_PREFERRED = -1;
- }
-
- public static int[] getAsArray(ArrayList<Integer> list) {
- int[] array = new int[list.size()];
- for (int i = 0; i < list.size(); i++) {
- array[i] = list.get(i);
- }
- return array;
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index e264b4678..1e51403fc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.webkit.MimeTypeMap;
+import org.openintents.openpgp.OpenPgpDecryptionResult;
import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream;
@@ -283,10 +284,6 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
PGPSignatureList signatureList = (PGPSignatureList) pgpF.nextObject();
PGPSignature messageSignature = signatureList.get(signatureIndex);
- // these are not cleartext signatures!
- // TODO: what about binary signatures?
- signatureResultBuilder.setSignatureOnly(false);
-
// Verify signature and check binding signatures
boolean validSignature = signature.verify(messageSignature);
if (validSignature) {
@@ -298,8 +295,8 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
OpenPgpSignatureResult signatureResult = signatureResultBuilder.build();
- if (signatureResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED
- && signatureResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED) {
+ if (signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_CONFIRMED
+ && signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED) {
log.add(LogType.MSG_VL_ERROR_INTEGRITY_CHECK, indent);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
@@ -309,9 +306,10 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
log.add(LogType.MSG_VL_OK, indent);
// Return a positive result, with metadata and verification info
- DecryptVerifyResult result =
- new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setSignatureResult(signatureResult);
+ result.setDecryptionResult(
+ new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
return result;
}
@@ -322,6 +320,8 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
InputStream in, OutputStream out, int indent) throws IOException, PGPException {
+ OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
+ OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
OperationLog log = new OperationLog();
log.add(LogType.MSG_DC, indent);
@@ -464,6 +464,12 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
}
}
+ // check for insecure encryption key
+ if ( ! PgpSecurityConstants.isSecureKey(secretEncryptionKey)) {
+ log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
+ decryptionResultBuilder.setInsecure(true);
+ }
+
// break out of while, only decrypt the first packet where we have a key
break;
@@ -614,15 +620,16 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
+ decryptionResultBuilder.setEncrypted(true);
- // Warn about old encryption algorithms!
- if (!PgpConstants.sPreferredSymmetricAlgorithms.contains(symmetricEncryptionAlgo)) {
- log.add(LogType.MSG_DC_OLD_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
+ // Check for insecure encryption algorithms!
+ if (!PgpSecurityConstants.isSecureSymmetricAlgorithm(symmetricEncryptionAlgo)) {
+ log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
+ decryptionResultBuilder.setInsecure(true);
}
JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
- OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
int signatureIndex = -1;
CanonicalizedPublicKeyRing signingRing = null;
CanonicalizedPublicKey signingKey = null;
@@ -686,6 +693,13 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
}
}
+ // check for insecure signing key
+ // TODO: checks on signingRing ?
+ if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) {
+ log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
+ signatureResultBuilder.setInsecure(true);
+ }
+
dataChunk = plainFact.nextObject();
}
@@ -752,7 +766,7 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setCharset(charset);
- result.setDecryptMetadata(metadata);
+ result.setDecryptionMetadata(metadata);
return result;
}
@@ -809,11 +823,9 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
PGPSignature messageSignature = signatureList.get(signatureIndex);
- // these are not cleartext signatures!
// TODO: what about binary signatures?
- signatureResultBuilder.setSignatureOnly(false);
- // Verify signature and check binding signatures
+ // Verify signature
boolean validSignature = signature.verify(messageSignature);
if (validSignature) {
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
@@ -821,10 +833,10 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
}
- // Don't allow verification of old hash algorithms!
- if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
- validSignature = false;
- log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
+ // check for insecure hash algorithms
+ if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
+ log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
+ signatureResultBuilder.setInsecure(true);
}
signatureResultBuilder.setValidSignature(validSignature);
@@ -851,8 +863,8 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
// The MDC packet can be stripped by an attacker!
Log.d(Constants.TAG, "MDC fail");
if (!signatureResultBuilder.isValidSignature()) {
- log.add(LogType.MSG_DC_ERROR_INTEGRITY_MISSING, indent);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent);
+ decryptionResultBuilder.setInsecure(true);
}
}
@@ -861,12 +873,12 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
log.add(LogType.MSG_DC_OK, indent);
// Return a positive result, with metadata and verification info
- DecryptVerifyResult result =
- new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setCachedCryptoInputParcel(cryptoInput);
- result.setDecryptMetadata(metadata);
result.setSignatureResult(signatureResultBuilder.build());
result.setCharset(charset);
+ result.setDecryptionResult(decryptionResultBuilder.build());
+ result.setDecryptionMetadata(metadata);
return result;
}
@@ -885,8 +897,6 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
OperationLog log = new OperationLog();
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
- // cleartext signatures are never encrypted ;)
- signatureResultBuilder.setSignatureOnly(true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -924,7 +934,7 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
- PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder);
+ PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent);
if (signature != null) {
try {
@@ -956,10 +966,10 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
}
- // Don't allow verification of old hash algorithms!
- if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
- validSignature = false;
- log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
+ // check for insecure hash algorithms
+ if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
+ log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
+ signatureResultBuilder.setInsecure(true);
}
signatureResultBuilder.setValidSignature(validSignature);
@@ -981,8 +991,10 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
clearText.length);
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
- result.setDecryptMetadata(metadata);
result.setSignatureResult(signatureResultBuilder.build());
+ result.setDecryptionResult(
+ new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
+ result.setDecryptionMetadata(metadata);
return result;
}
@@ -994,8 +1006,6 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
OperationLog log = new OperationLog();
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
- // detached signatures are never encrypted
- signatureResultBuilder.setSignatureOnly(true);
updateProgress(R.string.progress_processing_signature, 0, 100);
InputStream detachedSigIn = new ByteArrayInputStream(input.getDetachedSignature());
@@ -1016,7 +1026,7 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
- PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder);
+ PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent);
if (signature != null) {
updateProgress(R.string.progress_reading_data, 60, 100);
@@ -1050,9 +1060,6 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
updateProgress(R.string.progress_verifying_signature, 90, 100);
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
- // these are not cleartext signatures!
- signatureResultBuilder.setSignatureOnly(false);
-
// Verify signature and check binding signatures
boolean validSignature = signature.verify();
if (validSignature) {
@@ -1061,10 +1068,10 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
}
- // Don't allow verification of old hash algorithms!
- if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
- validSignature = false;
- log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
+ // check for insecure hash algorithms
+ if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
+ log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
+ signatureResultBuilder.setInsecure(true);
}
signatureResultBuilder.setValidSignature(validSignature);
@@ -1076,11 +1083,14 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setSignatureResult(signatureResultBuilder.build());
+ result.setDecryptionResult(
+ new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
return result;
}
private PGPSignature processPGPSignatureList(
- PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder)
+ PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder,
+ OperationLog log, int indent)
throws PGPException {
CanonicalizedPublicKeyRing signingRing = null;
CanonicalizedPublicKey signingKey = null;
@@ -1122,6 +1132,13 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
}
}
+ // check for insecure signing key
+ // TODO: checks on signingRing ?
+ if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) {
+ log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
+ signatureResultBuilder.setInsecure(true);
+ }
+
return signature;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index c82cbce8f..6f156c201 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -316,14 +316,14 @@ public class PgpKeyOperation {
// Build key encrypter and decrypter based on passphrase
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
+ .build().get(PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO,
- encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO,
+ encryptorHashCalc, PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
+ .build().get(PgpSecurityConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
PGPSecretKey masterSecretKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, true, keyEncryptor);
@@ -1021,15 +1021,15 @@ public class PgpKeyOperation {
PGPSecretKey sKey; {
// Build key encrypter and decrypter based on passphrase
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
+ .build().get(PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
- PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
cryptoInput.getPassphrase().getCharArray());
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
+ .build().get(PgpSecurityConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, sha1Calc, false, keyEncryptor);
}
@@ -1206,7 +1206,7 @@ public class PgpKeyOperation {
// add packet with EMPTY notation data (updates old one, but will be stripped later)
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
- PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
+ PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
{ // set subpackets
@@ -1233,7 +1233,7 @@ public class PgpKeyOperation {
// add packet with "pin" notation data
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
- PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
+ PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
{ // set subpackets
@@ -1280,13 +1280,13 @@ public class PgpKeyOperation {
OperationLog log, int indent) throws PGPException {
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
- .get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
+ .get(PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
// Build key encryptor based on new passphrase
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
- PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
- PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(newPassphrase.getCharArray());
// noinspection unchecked
@@ -1440,13 +1440,13 @@ public class PgpKeyOperation {
if (divertToCard) {
// use synchronous "NFC based" SignerBuilder
builder = new NfcSyncPGPContentSignerBuilder(
- pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO,
+ pKey.getAlgorithm(), PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO,
pKey.getKeyID(), cryptoInput.getCryptoData())
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
} else {
// content signer based on signing key algorithm and chosen hash algorithm
builder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
+ pKey.getAlgorithm(), PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
}
@@ -1472,11 +1472,11 @@ public class PgpKeyOperation {
*/
/* non-critical subpackets: */
hashedPacketsGen.setPreferredSymmetricAlgorithms(false,
- PgpConstants.getAsArray(PgpConstants.sPreferredSymmetricAlgorithms));
+ PgpSecurityConstants.PREFERRED_SYMMETRIC_ALGORITHMS);
hashedPacketsGen.setPreferredHashAlgorithms(false,
- PgpConstants.getAsArray(PgpConstants.sPreferredHashAlgorithms));
+ PgpSecurityConstants.PREFERRED_HASH_ALGORITHMS);
hashedPacketsGen.setPreferredCompressionAlgorithms(false,
- PgpConstants.getAsArray(PgpConstants.sPreferredCompressionAlgorithms));
+ PgpSecurityConstants.PREFERRED_COMPRESSION_ALGORITHMS);
hashedPacketsGen.setPrimaryUserID(false, primary);
/* critical subpackets: we consider those important for a modern pgp implementation */
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSecurityConstants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSecurityConstants.java
new file mode 100644
index 000000000..94fb2d877
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSecurityConstants.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.asn1.nist.NISTNamedCurves;
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+
+import java.util.HashSet;
+
+/**
+ * NIST requirements for 2011-2030 (http://www.keylength.com/en/4/):
+ * - RSA: 2048 bit
+ * - ECC: 224 bit
+ * - Symmetric: 3TDEA
+ * - Digital Signature (hash A): SHA-224 - SHA-512
+ *
+ * Extreme Decisions for Yahoo's End-to-End:
+ * https://github.com/yahoo/end-to-end/issues/31
+ * https://gist.github.com/coruus/68a8c65571e2b4225a69
+ */
+public class PgpSecurityConstants {
+
+ /**
+ * Whitelist of accepted symmetric encryption algorithms
+ * all other algorithms are rejected with OpenPgpDecryptionResult.RESULT_INSECURE
+ */
+ private static HashSet<Integer> sSymmetricAlgorithmsWhitelist = new HashSet<>();
+ static {
+ // General remarks: We try to keep the whitelist short to reduce attack surface
+ // TODO: block IDEA?: Bad key schedule (weak keys), implementation difficulties (easy to make errors)
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.IDEA);
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.TRIPLE_DES); // a MUST in RFC
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.CAST5); // default in many gpg, pgp versions, 128 bit key
+ // BLOWFISH: Twofish is the successor
+ // SAFER: not used widely
+ // DES: < 128 bit security
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.AES_128);
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.AES_192);
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.AES_256);
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.TWOFISH); // 128 bit
+ // CAMELLIA_128: not used widely
+ // CAMELLIA_192: not used widely
+ // CAMELLIA_256: not used widely
+ }
+
+ public static boolean isSecureSymmetricAlgorithm(int id) {
+ return sSymmetricAlgorithmsWhitelist.contains(id);
+ }
+
+ /**
+ * Whitelist of accepted hash algorithms
+ * all other algorithms are rejected with OpenPgpSignatureResult.RESULT_INSECURE
+ *
+ * coorus:
+ * Implementations SHOULD use SHA-512 for RSA or DSA signatures. They SHOULD NOT use SHA-384.
+ * ((cite to affine padding attacks; unproven status of RSA-PKCSv15))
+ *
+ * Implementations MUST NOT sign SHA-224 hashes. They SHOULD NOT accept signatures over SHA-224 hashes.
+ * ((collision resistance of 112-bits))
+ * Implementations SHOULD NOT sign SHA-256 hashes. They MUST NOT default to signing SHA-256 hashes.
+ */
+ private static HashSet<Integer> sHashAlgorithmsWhitelist = new HashSet<>();
+ static {
+ // MD5: broken
+ // SHA1: broken
+ // RIPEMD160: same security properties as SHA1
+ // DOUBLE_SHA: not used widely
+ // MD2: not used widely
+ // TIGER_192: not used widely
+ // HAVAL_5_160: not used widely
+ sHashAlgorithmsWhitelist.add(HashAlgorithmTags.SHA256); // compatibility for old Mailvelope versions
+ sHashAlgorithmsWhitelist.add(HashAlgorithmTags.SHA384);
+ sHashAlgorithmsWhitelist.add(HashAlgorithmTags.SHA512);
+ // SHA224: Not used widely, Yahoo argues against it
+ }
+
+ public static boolean isSecureHashAlgorithm(int id) {
+ return sHashAlgorithmsWhitelist.contains(id);
+ }
+
+ /**
+ * Whitelist of accepted asymmetric algorithms in switch statement
+ * all other algorithms are rejected with OpenPgpSignatureResult.RESULT_INSECURE or
+ * OpenPgpDecryptionResult.RESULT_INSECURE
+ *
+ * coorus:
+ * Implementations MUST NOT accept, or treat any signature as valid, by an RSA key with
+ * bitlength less than 1023 bits.
+ * Implementations MUST NOT accept any RSA keys with bitlength less than 2047 bits after January 1, 2016.
+ */
+ private static HashSet<String> sCurveWhitelist = new HashSet<>();
+ static {
+ sCurveWhitelist.add(NISTNamedCurves.getOID("P-256").getId());
+ sCurveWhitelist.add(NISTNamedCurves.getOID("P-384").getId());
+ sCurveWhitelist.add(NISTNamedCurves.getOID("P-521").getId());
+ }
+
+ public static boolean isSecureKey(CanonicalizedPublicKey key) {
+ switch (key.getAlgorithm()) {
+ case PublicKeyAlgorithmTags.RSA_GENERAL: {
+ return (key.getBitStrength() >= 2048);
+ }
+ // RSA_ENCRYPT, RSA_SIGN: deprecated in RFC 4880, use RSA_GENERAL with key flags
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: {
+ return (key.getBitStrength() >= 2048);
+ }
+ case PublicKeyAlgorithmTags.DSA: {
+ return (key.getBitStrength() >= 2048);
+ }
+ case PublicKeyAlgorithmTags.ECDH:
+ case PublicKeyAlgorithmTags.ECDSA: {
+ return PgpSecurityConstants.sCurveWhitelist.contains(key.getCurveOid());
+ }
+ // ELGAMAL_GENERAL: deprecated in RFC 4880, use ELGAMAL_ENCRYPT
+ // DIFFIE_HELLMAN: unsure
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * These array is written as a list of preferred encryption algorithms into keys created by us.
+ * Other implementations may choose to honor this selection.
+ * (Most preferred is first)
+ *
+ * REASON: See corresponding whitelist. AES received most cryptanalysis over the years
+ * and is still secure!
+ */
+ public static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
+ SymmetricKeyAlgorithmTags.AES_256,
+ SymmetricKeyAlgorithmTags.AES_192,
+ SymmetricKeyAlgorithmTags.AES_128,
+ };
+
+ /**
+ * These array is written as a list of preferred hash algorithms into keys created by us.
+ * Other implementations may choose to honor this selection.
+ * (Most preferred is first)
+ *
+ * REASON: See corresponding whitelist. If possible use SHA-512, this is state of the art!
+ */
+ public static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{
+ HashAlgorithmTags.SHA512,
+ };
+
+ /**
+ * These array is written as a list of preferred compression algorithms into keys created by us.
+ * Other implementations may choose to honor this selection.
+ * (Most preferred is first)
+ *
+ * REASON: See DEFAULT_COMPRESSION_ALGORITHM
+ */
+ public static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{
+ CompressionAlgorithmTags.ZIP,
+ };
+
+ /**
+ * Hash algorithm used to certify public keys
+ */
+ public static final int CERTIFY_HASH_ALGO = HashAlgorithmTags.SHA512;
+
+
+ /**
+ * Always use AES-256! We always ignore the preferred encryption algos of the recipient!
+ *
+ * coorus:
+ * Implementations SHOULD ignore the symmetric algorithm preferences of a recipient's public key;
+ * in particular, implementations MUST NOT choose an algorithm forbidden by this
+ * document because a recipient prefers it.
+ *
+ * NEEDCITE downgrade attacks on TLS, other protocols
+ */
+ public static final int DEFAULT_SYMMETRIC_ALGORITHM = SymmetricKeyAlgorithmTags.AES_256;
+
+ public interface OpenKeychainSymmetricKeyAlgorithmTags extends SymmetricKeyAlgorithmTags {
+ int USE_DEFAULT = -1;
+ }
+
+ /**
+ * Always use SHA-512! We always ignore the preferred hash algos of the recipient!
+ *
+ * coorus:
+ * Implementations MUST ignore the hash algorithm preferences of a recipient when signing
+ * a message to a recipient. The difficulty of forging a signature under a given key,
+ * using generic attacks on hash functions, is the difficulty of the weakest hash signed by that key.
+ *
+ * Implementations MUST default to using SHA-512 for RSA signatures,
+ *
+ * and either SHA-512 or the matched instance of SHA-2 for ECDSA signatures.
+ * TODO: Ed25519
+ * CITE: zooko's hash function table CITE: distinguishers on SHA-256
+ */
+ public static final int DEFAULT_HASH_ALGORITHM = HashAlgorithmTags.SHA512;
+
+ public interface OpenKeychainHashAlgorithmTags extends HashAlgorithmTags {
+ int USE_DEFAULT = -1;
+ }
+
+ /**
+ * Compression is disabled by default.
+ *
+ * The default compression algorithm is only used if explicitly enabled in the activity's
+ * overflow menu or via the OpenPGP API's extra OpenPgpApi.EXTRA_ENABLE_COMPRESSION
+ *
+ * REASON: Enabling compression can lead to a sidechannel. Consider a voting that is done via
+ * OpenPGP. Compression can lead to different ciphertext lengths based on the user's voting.
+ * This has happened in a voting done by Wikipedia (Google it).
+ *
+ * ZLIB: the format provides no benefits over DEFLATE, and is more malleable
+ * BZIP2: very slow
+ */
+ public static final int DEFAULT_COMPRESSION_ALGORITHM = CompressionAlgorithmTags.ZIP;
+
+ public interface OpenKeychainCompressionAlgorithmTags extends CompressionAlgorithmTags {
+ int USE_DEFAULT = -1;
+ }
+
+ /**
+ * Note: s2kcount is a number between 0 and 0xff that controls the
+ * number of times to iterate the password hash before use. More
+ * iterations are useful against offline attacks, as it takes more
+ * time to check each password. The actual number of iterations is
+ * rather complex, and also depends on the hash function in use.
+ * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
+ * you more iterations. As a rough rule of thumb, when using
+ * SHA256 as the hashing function, 0x10 gives you about 64
+ * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
+ * or about 1 million iterations. The maximum you can go to is
+ * 0xff, or about 2 million iterations.
+ * from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
+ *
+ * Bouncy Castle default: 0x60
+ * kbsriram proposes: 0xc0
+ * Yahoo's End-to-End: 96 (65536 iterations) (https://github.com/yahoo/end-to-end/blob/master/src/javascript/crypto/e2e/openpgp/keyring.js)
+ */
+ public static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 96;
+ public static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA512;
+ public static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
+ public static final int SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA512;
+ // NOTE: only SHA1 is supported for key checksum calculations in OpenPGP,
+ // see http://tools.ietf.org/html/rfc488 0#section-5.5.3
+ public static final int SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO = HashAlgorithmTags.SHA1;
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
index fa6268758..36d1a07cb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
@@ -30,19 +30,20 @@ public class PgpSignEncryptInputParcel implements Parcelable {
protected String mVersionHeader = null;
protected boolean mEnableAsciiArmorOutput = false;
- protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
+ protected int mCompressionAlgorithm = CompressionAlgorithmTags.UNCOMPRESSED;
protected long[] mEncryptionMasterKeyIds = null;
protected Passphrase mSymmetricPassphrase = null;
- protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED;
+ protected int mSymmetricEncryptionAlgorithm = PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT;
protected long mSignatureMasterKeyId = Constants.key.none;
protected Long mSignatureSubKeyId = null;
- protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
+ protected int mSignatureHashAlgorithm = PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT;
protected long mAdditionalEncryptId = Constants.key.none;
protected boolean mFailOnMissingEncryptionKeyIds = false;
protected String mCharset;
protected boolean mCleartextSignature;
protected boolean mDetachedSignature = false;
protected boolean mHiddenRecipients = false;
+ protected boolean mIntegrityProtected = true;
public PgpSignEncryptInputParcel() {
@@ -55,7 +56,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
// we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
mVersionHeader = source.readString();
mEnableAsciiArmorOutput = source.readInt() == 1;
- mCompressionId = source.readInt();
+ mCompressionAlgorithm = source.readInt();
mEncryptionMasterKeyIds = source.createLongArray();
mSymmetricPassphrase = source.readParcelable(loader);
mSymmetricEncryptionAlgorithm = source.readInt();
@@ -68,6 +69,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
mCleartextSignature = source.readInt() == 1;
mDetachedSignature = source.readInt() == 1;
mHiddenRecipients = source.readInt() == 1;
+ mIntegrityProtected = source.readInt() == 1;
}
@Override
@@ -79,7 +81,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mVersionHeader);
dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
- dest.writeInt(mCompressionId);
+ dest.writeInt(mCompressionAlgorithm);
dest.writeLongArray(mEncryptionMasterKeyIds);
dest.writeParcelable(mSymmetricPassphrase, 0);
dest.writeInt(mSymmetricEncryptionAlgorithm);
@@ -97,6 +99,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
dest.writeInt(mCleartextSignature ? 1 : 0);
dest.writeInt(mDetachedSignature ? 1 : 0);
dest.writeInt(mHiddenRecipients ? 1 : 0);
+ dest.writeInt(mIntegrityProtected ? 1 : 0);
}
public String getCharset() {
@@ -174,12 +177,12 @@ public class PgpSignEncryptInputParcel implements Parcelable {
return this;
}
- public int getCompressionId() {
- return mCompressionId;
+ public int getCompressionAlgorithm() {
+ return mCompressionAlgorithm;
}
- public PgpSignEncryptInputParcel setCompressionId(int compressionId) {
- mCompressionId = compressionId;
+ public PgpSignEncryptInputParcel setCompressionAlgorithm(int compressionAlgorithm) {
+ mCompressionAlgorithm = compressionAlgorithm;
return this;
}
@@ -229,6 +232,18 @@ public class PgpSignEncryptInputParcel implements Parcelable {
return this;
}
+ public boolean isIntegrityProtected() {
+ return mIntegrityProtected;
+ }
+
+ /**
+ * Only use for testing! Never disable integrity protection!
+ */
+ public PgpSignEncryptInputParcel setIntegrityProtected(boolean integrityProtected) {
+ this.mIntegrityProtected = integrityProtected;
+ return this;
+ }
+
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
index 8fb41a909..29b2ef727 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
@@ -62,7 +62,6 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Set;
@@ -124,7 +123,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
boolean enableSignature = input.getSignatureMasterKeyId() != Constants.key.none;
boolean enableEncryption = ((input.getEncryptionMasterKeyIds() != null && input.getEncryptionMasterKeyIds().length > 0)
|| input.getSymmetricPassphrase() != null);
- boolean enableCompression = (input.getCompressionId() != CompressionAlgorithmTags.UNCOMPRESSED);
+ boolean enableCompression = (input.getCompressionAlgorithm() != CompressionAlgorithmTags.UNCOMPRESSED);
Log.d(Constants.TAG, "enableSignature:" + enableSignature
+ "\nenableEncryption:" + enableEncryption
@@ -226,15 +225,10 @@ public class PgpSignEncryptOperation extends BaseOperation {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
- // Use preferred hash algo
+ // Use requested hash algo
int requestedAlgorithm = input.getSignatureHashAlgorithm();
- ArrayList<Integer> supported = signingKey.getSupportedHashAlgorithms();
- if (requestedAlgorithm == PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED) {
- // get most preferred
- input.setSignatureHashAlgorithm(supported.get(0));
- } else if (!supported.contains(requestedAlgorithm)) {
- log.add(LogType.MSG_PSE_ERROR_HASH_ALGO, indent);
- return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
+ if (requestedAlgorithm == PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT) {
+ input.setSignatureHashAlgorithm(PgpSecurityConstants.DEFAULT_HASH_ALGORITHM);
}
}
updateProgress(R.string.progress_preparing_streams, 2, 100);
@@ -243,18 +237,15 @@ public class PgpSignEncryptOperation extends BaseOperation {
PGPEncryptedDataGenerator cPk = null;
if (enableEncryption) {
- // Use preferred encryption algo
+ // Use requested encryption algo
int algo = input.getSymmetricEncryptionAlgorithm();
- if (algo == PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED) {
- // get most preferred
- // TODO: get from recipients
- algo = PgpConstants.sPreferredSymmetricAlgorithms.get(0);
+ if (algo == PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT) {
+ algo = PgpSecurityConstants.DEFAULT_SYMMETRIC_ALGORITHM;
}
- // has Integrity packet enabled!
JcePGPDataEncryptorBuilder encryptorBuilder =
new JcePGPDataEncryptorBuilder(algo)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
- .setWithIntegrityPacket(true);
+ .setWithIntegrityPacket(input.isIntegrityProtected());
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
@@ -341,7 +332,13 @@ public class PgpSignEncryptOperation extends BaseOperation {
if (enableCompression) {
log.add(LogType.MSG_PSE_COMPRESSING, indent);
- compressGen = new PGPCompressedDataGenerator(input.getCompressionId());
+
+ // Use preferred compression algo
+ int algo = input.getCompressionAlgorithm();
+ if (algo == PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT) {
+ algo = PgpSecurityConstants.DEFAULT_COMPRESSION_ALGORITHM;
+ }
+ compressGen = new PGPCompressedDataGenerator(algo);
bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
} else {
bcpgOut = new BCPGOutputStream(encryptionOut);
@@ -464,7 +461,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
InputStream in = inputData.getInputStream();
if (enableCompression) {
- compressGen = new PGPCompressedDataGenerator(input.getCompressionId());
+ compressGen = new PGPCompressedDataGenerator(input.getCompressionAlgorithm());
bcpgOut = new BCPGOutputStream(compressGen.open(out));
} else {
bcpgOut = new BCPGOutputStream(out);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
index 9276cba10..26f046372 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -211,12 +211,19 @@ public class UncachedPublicKey {
return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
}
+ public boolean isRSA() {
+ return getAlgorithm() == PGPPublicKey.RSA_GENERAL
+ || getAlgorithm() == PGPPublicKey.RSA_ENCRYPT
+ || getAlgorithm() == PGPPublicKey.RSA_SIGN;
+ }
+
public boolean isDSA() {
return getAlgorithm() == PGPPublicKey.DSA;
}
public boolean isEC() {
- return getAlgorithm() == PGPPublicKey.ECDH || getAlgorithm() == PGPPublicKey.ECDSA;
+ return getAlgorithm() == PGPPublicKey.ECDH
+ || getAlgorithm() == PGPPublicKey.ECDSA;
}
public byte[] getFingerprint() {