Merge branch 'development' into linked-identities
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
This commit is contained in:
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.bcpg.S2K;
|
||||
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
@@ -29,6 +30,7 @@ import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||
@@ -44,6 +46,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
@@ -137,7 +140,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
// It means the passphrase is empty
|
||||
return SecretKeyType.PASSPHRASE_EMPTY;
|
||||
} catch (PGPException e) {
|
||||
HashMap<String,String> notation = getRing().getLocalNotationData();
|
||||
HashMap<String, String> notation = getRing().getLocalNotationData();
|
||||
if (notation.containsKey("unlock.pin@sufficientlysecure.org")
|
||||
&& "1".equals(notation.get("unlock.pin@sufficientlysecure.org"))) {
|
||||
return SecretKeyType.PIN;
|
||||
@@ -176,33 +179,13 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all supported hash algorithms. This list is currently hardcoded to return
|
||||
* a limited set of algorithms supported by Yubikeys.
|
||||
*
|
||||
* @return
|
||||
* Returns a list of all supported hash algorithms.
|
||||
*/
|
||||
public LinkedList<Integer> getSupportedHashAlgorithms() {
|
||||
LinkedList<Integer> supported = new LinkedList<>();
|
||||
public ArrayList<Integer> getSupportedHashAlgorithms() {
|
||||
// TODO: intersection between preferred hash algos of this key and PgpConstants.PREFERRED_HASH_ALGORITHMS
|
||||
// choose best algo
|
||||
|
||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
||||
// No support for MD5
|
||||
supported.add(HashAlgorithmTags.RIPEMD160);
|
||||
supported.add(HashAlgorithmTags.SHA1);
|
||||
supported.add(HashAlgorithmTags.SHA224);
|
||||
supported.add(HashAlgorithmTags.SHA256);
|
||||
supported.add(HashAlgorithmTags.SHA384);
|
||||
supported.add(HashAlgorithmTags.SHA512); // preferred is latest
|
||||
} else {
|
||||
supported.add(HashAlgorithmTags.MD5);
|
||||
supported.add(HashAlgorithmTags.RIPEMD160);
|
||||
supported.add(HashAlgorithmTags.SHA1);
|
||||
supported.add(HashAlgorithmTags.SHA224);
|
||||
supported.add(HashAlgorithmTags.SHA256);
|
||||
supported.add(HashAlgorithmTags.SHA384);
|
||||
supported.add(HashAlgorithmTags.SHA512); // preferred is latest
|
||||
}
|
||||
|
||||
return supported;
|
||||
return PgpConstants.sPreferredHashAlgorithms;
|
||||
}
|
||||
|
||||
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, byte[] nfcSignedHash,
|
||||
@@ -286,7 +269,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
* Certify the given pubkeyid with the given masterkeyid.
|
||||
*
|
||||
* @param publicKeyRing Keyring to add certification to.
|
||||
* @param userIds User IDs to certify, or all if null
|
||||
* @param userIds User IDs to certify
|
||||
* @return A keyring with added certifications
|
||||
*/
|
||||
public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds,
|
||||
@@ -331,10 +314,8 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
|
||||
|
||||
// fetch public key ring, add the certification and return it
|
||||
Iterable<String> it = userIds != null ? userIds
|
||||
: new IterableIterator<String>(publicKey.getUserIDs());
|
||||
try {
|
||||
for (String userId : it) {
|
||||
for (String userId : userIds) {
|
||||
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
|
||||
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
|
||||
}
|
||||
@@ -348,6 +329,71 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
return new UncachedKeyRing(ring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Certify the given user attributes with the given masterkeyid.
|
||||
*
|
||||
* @param publicKeyRing Keyring to add certification to.
|
||||
* @param userAttributes User IDs to certify, or all if null
|
||||
* @return A keyring with added certifications
|
||||
*/
|
||||
public UncachedKeyRing certifyUserAttributes(CanonicalizedPublicKeyRing publicKeyRing,
|
||||
List<WrappedUserAttribute> userAttributes, byte[] nfcSignedHash, Date nfcCreationTimestamp) {
|
||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||
throw new PrivateKeyNotUnlockedException();
|
||||
}
|
||||
if (!isMasterKey()) {
|
||||
throw new AssertionError("tried to certify with non-master key, this is a programming error!");
|
||||
}
|
||||
if (publicKeyRing.getMasterKeyId() == getKeyId()) {
|
||||
throw new AssertionError("key tried to self-certify, this is a programming error!");
|
||||
}
|
||||
|
||||
// create a signatureGenerator from the supplied masterKeyId and passphrase
|
||||
PGPSignatureGenerator signatureGenerator;
|
||||
{
|
||||
// TODO: SHA256 fixed?
|
||||
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(PGPUtil.SHA256,
|
||||
nfcSignedHash, nfcCreationTimestamp);
|
||||
|
||||
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||
try {
|
||||
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
|
||||
} catch (PGPException e) {
|
||||
Log.e(Constants.TAG, "signing error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
{ // supply signatureGenerator with a SubpacketVector
|
||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||
if (nfcCreationTimestamp != null) {
|
||||
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
|
||||
Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp);
|
||||
}
|
||||
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
||||
signatureGenerator.setHashedSubpackets(packetVector);
|
||||
}
|
||||
|
||||
// get the master subkey (which we certify for)
|
||||
PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
|
||||
|
||||
// fetch public key ring, add the certification and return it
|
||||
try {
|
||||
for (WrappedUserAttribute userAttribute : userAttributes) {
|
||||
PGPUserAttributeSubpacketVector vector = userAttribute.getVector();
|
||||
PGPSignature sig = signatureGenerator.generateCertification(vector, publicKey);
|
||||
publicKey = PGPPublicKey.addCertification(publicKey, vector, sig);
|
||||
}
|
||||
} catch (PGPException e) {
|
||||
Log.e(Constants.TAG, "signing error", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
|
||||
|
||||
return new UncachedKeyRing(ring);
|
||||
}
|
||||
|
||||
static class PrivateKeyNotUnlockedException extends RuntimeException {
|
||||
// this exception is a programming error which happens when an operation which requires
|
||||
// the private key is called without a previous call to unlock()
|
||||
@@ -358,7 +404,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
}
|
||||
|
||||
// HACK, for TESTING ONLY!!
|
||||
PGPPrivateKey getPrivateKey () {
|
||||
PGPPrivateKey getPrivateKey() {
|
||||
return mPrivateKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
@@ -45,7 +45,7 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
|
||||
public CanonicalizedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
|
||||
{
|
||||
super(verified);
|
||||
PGPObjectFactory factory = new PGPObjectFactory(blob);
|
||||
JcaPGPObjectFactory factory = new JcaPGPObjectFactory(blob);
|
||||
PGPKeyRing keyRing = null;
|
||||
try {
|
||||
if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PgpConstants {
|
||||
|
||||
public static ArrayList<Integer> sPreferredSymmetricAlgorithms = new ArrayList<>();
|
||||
public static ArrayList<Integer> sPreferredHashAlgorithms = new ArrayList<>();
|
||||
public static ArrayList<Integer> sPreferredCompressionAlgorithms = new ArrayList<>();
|
||||
|
||||
// TODO: use hashmaps for contains in O(1) and intersections!
|
||||
|
||||
/*
|
||||
* Most preferred is first
|
||||
* These arrays are written as preferred algorithms into the keys on creation.
|
||||
* Other implementations may choose to honor this selection.
|
||||
*
|
||||
* These lists also define the only algorithms which are used in OpenKeychain.
|
||||
* We do not support algorithms such as MD5
|
||||
*/
|
||||
static {
|
||||
sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_256);
|
||||
sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_192);
|
||||
sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_128);
|
||||
sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.TWOFISH);
|
||||
|
||||
// NOTE: some implementations do not support SHA512, thus we choose SHA256 as default (Mailvelope?)
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA256);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA512);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA384);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA224);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA1);
|
||||
sPreferredHashAlgorithms.add(HashAlgorithmTags.RIPEMD160);
|
||||
|
||||
sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZLIB);
|
||||
sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.BZIP2);
|
||||
sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZIP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: s2kcount is a number between 0 and 0xff that controls the
|
||||
* number of times to iterate the password hash before use. More
|
||||
* iterations are useful against offline attacks, as it takes more
|
||||
* time to check each password. The actual number of iterations is
|
||||
* rather complex, and also depends on the hash function in use.
|
||||
* Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
|
||||
* you more iterations. As a rough rule of thumb, when using
|
||||
* SHA256 as the hashing function, 0x10 gives you about 64
|
||||
* iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
|
||||
* or about 1 million iterations. The maximum you can go to is
|
||||
* 0xff, or about 2 million iterations.
|
||||
* from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
|
||||
*
|
||||
* Bouncy Castle default: 0x60
|
||||
* kbsriram proposes: 0xc0
|
||||
* OpenKeychain: 0x90
|
||||
*/
|
||||
public static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90;
|
||||
public static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA256;
|
||||
public static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
|
||||
public static final int SECRET_KEY_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA256;
|
||||
// NOTE: only SHA1 is supported for key checksum calculations in OpenPGP,
|
||||
// see http://tools.ietf.org/html/rfc488 0#section-5.5.3
|
||||
public static final int SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO = HashAlgorithmTags.SHA1;
|
||||
|
||||
public static interface OpenKeychainSymmetricKeyAlgorithmTags extends SymmetricKeyAlgorithmTags {
|
||||
public static final int USE_PREFERRED = -1;
|
||||
}
|
||||
|
||||
public static interface OpenKeychainHashAlgorithmTags extends HashAlgorithmTags {
|
||||
public static final int USE_PREFERRED = -1;
|
||||
}
|
||||
|
||||
public static interface OpenKeychainCompressionAlgorithmTags extends CompressionAlgorithmTags {
|
||||
public static final int USE_PREFERRED = -1;
|
||||
}
|
||||
|
||||
public static int[] getAsArray(ArrayList<Integer> list) {
|
||||
int[] array = new int[list.size()];
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
array[i] = list.get(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
@@ -563,6 +563,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
log.add(LogType.MSG_DC_PREP_STREAMS, indent);
|
||||
|
||||
// we made sure above one of these two would be true
|
||||
int symmetricEncryptionAlgo;
|
||||
if (symmetricPacketFound) {
|
||||
currentProgress += 2;
|
||||
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
||||
@@ -576,6 +577,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
|
||||
encryptedData = encryptedDataSymmetric;
|
||||
|
||||
symmetricEncryptionAlgo = encryptedDataSymmetric.getSymmetricAlgorithm(decryptorFactory);
|
||||
} else if (asymmetricPacketFound) {
|
||||
currentProgress += 2;
|
||||
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
|
||||
@@ -598,6 +600,8 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
PublicKeyDataDecryptorFactory decryptorFactory
|
||||
= secretEncryptionKey.getDecryptorFactory(mDecryptedSessionKey);
|
||||
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
|
||||
|
||||
symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
|
||||
} catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {
|
||||
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
|
||||
DecryptVerifyResult result =
|
||||
@@ -614,6 +618,11 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
// Warn about old encryption algorithms!
|
||||
if (!PgpConstants.sPreferredSymmetricAlgorithms.contains(symmetricEncryptionAlgo)) {
|
||||
log.add(LogType.MSG_DC_OLD_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
|
||||
}
|
||||
|
||||
JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
|
||||
Object dataChunk = plainFact.nextObject();
|
||||
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
|
||||
@@ -811,6 +820,13 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
} else {
|
||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
||||
}
|
||||
|
||||
// Don't allow verification of old hash algorithms!
|
||||
if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
|
||||
validSignature = false;
|
||||
log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
|
||||
}
|
||||
|
||||
signatureResultBuilder.setValidSignature(validSignature);
|
||||
}
|
||||
|
||||
@@ -936,6 +952,13 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
} else {
|
||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
||||
}
|
||||
|
||||
// Don't allow verification of old hash algorithms!
|
||||
if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
|
||||
validSignature = false;
|
||||
log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
|
||||
}
|
||||
|
||||
signatureResultBuilder.setValidSignature(validSignature);
|
||||
|
||||
} catch (SignatureException e) {
|
||||
@@ -1024,6 +1047,13 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
} else {
|
||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
||||
}
|
||||
|
||||
// Don't allow verification of old hash algorithms!
|
||||
if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
|
||||
validSignature = false;
|
||||
log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
|
||||
}
|
||||
|
||||
signatureResultBuilder.setValidSignature(validSignature);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,26 +47,6 @@ public class PgpHelper {
|
||||
".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
|
||||
Pattern.DOTALL);
|
||||
|
||||
public static String getVersion(Context context) {
|
||||
String version;
|
||||
try {
|
||||
PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0);
|
||||
version = pi.versionName;
|
||||
return version;
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(Constants.TAG, "Version could not be retrieved!", e);
|
||||
return "0.0";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getVersionForHeader(Context context) {
|
||||
if(Preferences.getPreferences(context).getWriteVersionHeader()){
|
||||
return "OpenKeychain v" + getVersion(context);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes file securely by overwriting it with random data before deleting it.
|
||||
* <p/>
|
||||
|
||||
@@ -18,9 +18,7 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
import org.spongycastle.bcpg.sig.Features;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.spongycastle.jce.spec.ElGamalParameterSpec;
|
||||
@@ -90,49 +88,6 @@ public class PgpKeyOperation {
|
||||
private Stack<Progressable> mProgress;
|
||||
private AtomicBoolean mCancelled;
|
||||
|
||||
// most preferred is first
|
||||
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
|
||||
SymmetricKeyAlgorithmTags.AES_256,
|
||||
SymmetricKeyAlgorithmTags.AES_192,
|
||||
SymmetricKeyAlgorithmTags.AES_128,
|
||||
SymmetricKeyAlgorithmTags.CAST5
|
||||
};
|
||||
private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{
|
||||
HashAlgorithmTags.SHA512,
|
||||
HashAlgorithmTags.SHA384,
|
||||
HashAlgorithmTags.SHA256,
|
||||
HashAlgorithmTags.SHA224,
|
||||
HashAlgorithmTags.RIPEMD160
|
||||
};
|
||||
private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{
|
||||
CompressionAlgorithmTags.ZLIB,
|
||||
CompressionAlgorithmTags.BZIP2,
|
||||
CompressionAlgorithmTags.ZIP
|
||||
};
|
||||
|
||||
/*
|
||||
* Note: s2kcount is a number between 0 and 0xff that controls the
|
||||
* number of times to iterate the password hash before use. More
|
||||
* iterations are useful against offline attacks, as it takes more
|
||||
* time to check each password. The actual number of iterations is
|
||||
* rather complex, and also depends on the hash function in use.
|
||||
* Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
|
||||
* you more iterations. As a rough rule of thumb, when using
|
||||
* SHA256 as the hashing function, 0x10 gives you about 64
|
||||
* iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
|
||||
* or about 1 million iterations. The maximum you can go to is
|
||||
* 0xff, or about 2 million iterations.
|
||||
* from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
|
||||
*
|
||||
* Bouncy Castle default: 0x60
|
||||
* kbsriram proposes 0xc0
|
||||
* we use 0x90, a good trade-off between usability and security against offline attacks
|
||||
*/
|
||||
private static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90;
|
||||
private static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA256;
|
||||
private static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
|
||||
private static final int SECRET_KEY_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA256;
|
||||
|
||||
public PgpKeyOperation(Progressable progress) {
|
||||
super();
|
||||
if (progress != null) {
|
||||
@@ -346,14 +301,14 @@ public class PgpKeyOperation {
|
||||
|
||||
// Build key encrypter and decrypter based on passphrase
|
||||
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
.build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
|
||||
SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO,
|
||||
encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
|
||||
|
||||
// NOTE: only SHA1 is supported for key checksum calculations.
|
||||
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(HashAlgorithmTags.SHA1);
|
||||
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
|
||||
PGPSecretKey masterSecretKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
|
||||
sha1Calc, true, keyEncryptor);
|
||||
|
||||
@@ -880,14 +835,14 @@ public class PgpKeyOperation {
|
||||
PGPSecretKey sKey; {
|
||||
// Build key encrypter and decrypter based on passphrase
|
||||
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
.build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
|
||||
SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
|
||||
// NOTE: only SHA1 is supported for key checksum calculations.
|
||||
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(HashAlgorithmTags.SHA1);
|
||||
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
|
||||
sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, sha1Calc, false, keyEncryptor);
|
||||
}
|
||||
|
||||
@@ -1026,7 +981,8 @@ public class PgpKeyOperation {
|
||||
|
||||
// add packet with EMPTY notation data (updates old one, but will be stripped later)
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
{ // set subpackets
|
||||
@@ -1052,7 +1008,8 @@ public class PgpKeyOperation {
|
||||
|
||||
// add packet with "pin" notation data
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
{ // set subpackets
|
||||
@@ -1099,12 +1056,13 @@ public class PgpKeyOperation {
|
||||
OperationLog log, int indent) throws PGPException {
|
||||
|
||||
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
|
||||
.get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
.get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
// Build key encryptor based on new passphrase
|
||||
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
|
||||
SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||
newPassphrase.toCharArray());
|
||||
|
||||
@@ -1237,7 +1195,8 @@ public class PgpKeyOperation {
|
||||
int flags, long expiry)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
|
||||
@@ -1254,9 +1213,12 @@ public class PgpKeyOperation {
|
||||
* error than be ignored.
|
||||
*/
|
||||
/* non-critical subpackets: */
|
||||
hashedPacketsGen.setPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS);
|
||||
hashedPacketsGen.setPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS);
|
||||
hashedPacketsGen.setPreferredCompressionAlgorithms(false, PREFERRED_COMPRESSION_ALGORITHMS);
|
||||
hashedPacketsGen.setPreferredSymmetricAlgorithms(false,
|
||||
PgpConstants.getAsArray(PgpConstants.sPreferredSymmetricAlgorithms));
|
||||
hashedPacketsGen.setPreferredHashAlgorithms(false,
|
||||
PgpConstants.getAsArray(PgpConstants.sPreferredHashAlgorithms));
|
||||
hashedPacketsGen.setPreferredCompressionAlgorithms(false,
|
||||
PgpConstants.getAsArray(PgpConstants.sPreferredCompressionAlgorithms));
|
||||
hashedPacketsGen.setPrimaryUserID(false, primary);
|
||||
|
||||
/* critical subpackets: we consider those important for a modern pgp implementation */
|
||||
@@ -1280,7 +1242,8 @@ public class PgpKeyOperation {
|
||||
PGPUserAttributeSubpacketVector vector)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
|
||||
@@ -1299,7 +1262,8 @@ public class PgpKeyOperation {
|
||||
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
@@ -1313,7 +1277,7 @@ public class PgpKeyOperation {
|
||||
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPublicKey.getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
@@ -1357,7 +1321,7 @@ public class PgpKeyOperation {
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, creationTime);
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
pKey.getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
|
||||
@@ -1378,7 +1342,7 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPublicKey.getAlgorithm(), SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
|
||||
|
||||
@@ -12,10 +12,10 @@ public class PgpSignEncryptInput {
|
||||
protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
|
||||
protected long[] mEncryptionMasterKeyIds = null;
|
||||
protected String mSymmetricPassphrase = null;
|
||||
protected int mSymmetricEncryptionAlgorithm = 0;
|
||||
protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED;
|
||||
protected long mSignatureMasterKeyId = Constants.key.none;
|
||||
protected Long mSignatureSubKeyId = null;
|
||||
protected int mSignatureHashAlgorithm = 0;
|
||||
protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
|
||||
protected String mSignaturePassphrase = null;
|
||||
protected long mAdditionalEncryptId = Constants.key.none;
|
||||
protected byte[] mNfcSignedHash = null;
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||
import org.spongycastle.bcpg.BCPGOutputStream;
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.openpgp.PGPCompressedDataGenerator;
|
||||
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||
import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPLiteralData;
|
||||
@@ -58,6 +57,7 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
@@ -206,12 +206,12 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
// check if hash algo is supported
|
||||
// Use preferred hash algo
|
||||
int requestedAlgorithm = input.getSignatureHashAlgorithm();
|
||||
LinkedList<Integer> supported = signingKey.getSupportedHashAlgorithms();
|
||||
if (requestedAlgorithm == 0) {
|
||||
ArrayList<Integer> supported = signingKey.getSupportedHashAlgorithms();
|
||||
if (requestedAlgorithm == PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED) {
|
||||
// get most preferred
|
||||
input.setSignatureHashAlgorithm(supported.getLast());
|
||||
input.setSignatureHashAlgorithm(supported.get(0));
|
||||
} else if (!supported.contains(requestedAlgorithm)) {
|
||||
log.add(LogType.MSG_PSE_ERROR_HASH_ALGO, indent);
|
||||
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
||||
@@ -222,9 +222,13 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
/* Initialize PGPEncryptedDataGenerator for later usage */
|
||||
PGPEncryptedDataGenerator cPk = null;
|
||||
if (enableEncryption) {
|
||||
|
||||
// Use preferred encryption algo
|
||||
int algo = input.getSymmetricEncryptionAlgorithm();
|
||||
if (algo == 0) {
|
||||
algo = PGPEncryptedData.AES_128;
|
||||
if (algo == PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED) {
|
||||
// get most preferred
|
||||
// TODO: get from recipients
|
||||
algo = PgpConstants.sPreferredSymmetricAlgorithms.get(0);
|
||||
}
|
||||
// has Integrity packet enabled!
|
||||
JcePGPDataEncryptorBuilder encryptorBuilder =
|
||||
|
||||
Reference in New Issue
Block a user