Merge pull request #2111 from open-keychain/auto-value

Auto value
This commit is contained in:
Vincent Breitmoser
2017-05-25 19:42:02 +02:00
committed by GitHub
99 changed files with 2074 additions and 2798 deletions

View File

@@ -1,121 +0,0 @@
/*
* Copyright (C) 2016 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.util.Arrays;
import android.os.Parcel;
import android.os.Parcelable;
import org.bouncycastle.bcpg.S2K;
/** This is an immutable and parcelable class which stores the full s2k parametrization
* of an encrypted secret key, i.e. all fields of the {@link S2K} class (type, hash algo,
* iteration count, iv) plus the encryptionAlgorithm. This class is intended to be used
* as key in a HashMap for session key caching purposes, and overrides the
* {@link #hashCode} and {@link #equals} methods in a suitable way.
*
* Note that although it is a rather unlikely scenario that secret keys of the same key
* are encrypted with different ciphers, the encryption algorithm still determines the
* length of the specific session key and thus needs to be considered for purposes of
* session key caching.
*
* @see org.bouncycastle.bcpg.S2K
*/
public class ComparableS2K implements Parcelable {
private final int encryptionAlgorithm;
private final int s2kType;
private final int s2kHashAlgo;
private final long s2kItCount;
private final byte[] s2kIV;
Integer cachedHashCode;
public ComparableS2K(int encryptionAlgorithm, S2K s2k) {
this.encryptionAlgorithm = encryptionAlgorithm;
this.s2kType = s2k.getType();
this.s2kHashAlgo = s2k.getHashAlgorithm();
this.s2kItCount = s2k.getIterationCount();
this.s2kIV = s2k.getIV();
}
protected ComparableS2K(Parcel in) {
encryptionAlgorithm = in.readInt();
s2kType = in.readInt();
s2kHashAlgo = in.readInt();
s2kItCount = in.readLong();
s2kIV = in.createByteArray();
}
@Override
public int hashCode() {
if (cachedHashCode == null) {
cachedHashCode = encryptionAlgorithm;
cachedHashCode = 31 * cachedHashCode + s2kType;
cachedHashCode = 31 * cachedHashCode + s2kHashAlgo;
cachedHashCode = 31 * cachedHashCode + (int) (s2kItCount ^ (s2kItCount >>> 32));
cachedHashCode = 31 * cachedHashCode + Arrays.hashCode(s2kIV);
}
return cachedHashCode;
}
@Override
public boolean equals(Object o) {
boolean isComparableS2K = o instanceof ComparableS2K;
if (!isComparableS2K) {
return false;
}
ComparableS2K other = (ComparableS2K) o;
return encryptionAlgorithm == other.encryptionAlgorithm
&& s2kType == other.s2kType
&& s2kHashAlgo == other.s2kHashAlgo
&& s2kItCount == other.s2kItCount
&& Arrays.equals(s2kIV, other.s2kIV);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(encryptionAlgorithm);
dest.writeInt(s2kType);
dest.writeInt(s2kHashAlgo);
dest.writeLong(s2kItCount);
dest.writeByteArray(s2kIV);
}
public static final Creator<ComparableS2K> CREATOR = new Creator<ComparableS2K>() {
@Override
public ComparableS2K createFromParcel(Parcel in) {
return new ComparableS2K(in);
}
@Override
public ComparableS2K[] newArray(int size) {
return new ComparableS2K[size];
}
};
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 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 android.os.Parcelable;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import org.bouncycastle.bcpg.S2K;
/** This is an immutable and parcelable class which stores the full s2k parametrization
* of an encrypted secret key, i.e. all fields of the {@link S2K} class (type, hash algo,
* iteration count, iv) plus the encryptionAlgorithm. This class is intended to be used
* as key in a HashMap for session key caching purposes, and overrides the
* {@link #hashCode} and {@link #equals} methods in a suitable way.
*
* Note that although it is a rather unlikely scenario that secret keys of the same key
* are encrypted with different ciphers, the encryption algorithm still determines the
* length of the specific session key and thus needs to be considered for purposes of
* session key caching.
*
* @see org.bouncycastle.bcpg.S2K
*/
@AutoValue
public abstract class ParcelableS2K implements Parcelable {
abstract int getEncryptionAlgorithm();
abstract int getS2kType();
abstract int getS2kHashAlgo();
abstract long getS2kItCount();
@SuppressWarnings("mutable")
abstract byte[] getS2kIV();
@Memoized
@Override
public abstract int hashCode();
public static ParcelableS2K fromS2K(int encryptionAlgorithm, S2K s2k) {
return new AutoValue_ParcelableS2K(encryptionAlgorithm,
s2k.getType(), s2k.getHashAlgorithm(), s2k.getIterationCount(), s2k.getIV());
}
}

View File

@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
@@ -80,12 +81,13 @@ public class PgpCertifyOperation {
publicKey.getKeyID(), publicKey.getKeyID());
try {
if (action.mUserIds != null) {
log.add(LogType.MSG_CRT_CERTIFY_UIDS, 2, action.mUserIds.size(),
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
ArrayList<String> userIds = action.getUserIds();
if (userIds != null && !userIds.isEmpty()) {
log.add(LogType.MSG_CRT_CERTIFY_UIDS, 2, userIds.size(),
KeyFormattingUtils.convertKeyIdToHex(action.getMasterKeyId()));
// fetch public key ring, add the certification and return it
for (String userId : action.mUserIds) {
for (String userId : userIds) {
try {
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
@@ -96,12 +98,13 @@ public class PgpCertifyOperation {
}
if (action.mUserAttributes != null) {
log.add(LogType.MSG_CRT_CERTIFY_UATS, 2, action.mUserAttributes.size(),
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
ArrayList<WrappedUserAttribute> userAttributes = action.getUserAttributes();
if (userAttributes != null && !userAttributes.isEmpty()) {
log.add(LogType.MSG_CRT_CERTIFY_UATS, 2, userAttributes.size(),
KeyFormattingUtils.convertKeyIdToHex(action.getMasterKeyId()));
// fetch public key ring, add the certification and return it
for (WrappedUserAttribute userAttribute : action.mUserAttributes) {
for (WrappedUserAttribute userAttribute : userAttributes) {
PGPUserAttributeSubpacketVector vector = userAttribute.getVector();
try {
PGPSignature sig = signatureGenerator.generateCertification(vector, publicKey);

View File

@@ -19,152 +19,69 @@
package org.sufficientlysecure.keychain.pgp;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
public class PgpDecryptVerifyInputParcel implements Parcelable {
import com.google.auto.value.AutoValue;
private Uri mInputUri;
private Uri mOutputUri;
private byte[] mInputBytes;
private boolean mAllowSymmetricDecryption;
private HashSet<Long> mAllowedKeyIds;
private boolean mDecryptMetadataOnly;
private byte[] mDetachedSignature;
private String mRequiredSignerFingerprint;
private String mSenderAddress;
@AutoValue
public abstract class PgpDecryptVerifyInputParcel implements Parcelable {
@Nullable
@SuppressWarnings("mutable")
abstract byte[] getInputBytes();
public PgpDecryptVerifyInputParcel() {
@Nullable
abstract Uri getInputUri();
@Nullable
abstract Uri getOutputUri();
abstract boolean isAllowSymmetricDecryption();
abstract boolean isDecryptMetadataOnly();
@Nullable
abstract List<Long> getAllowedKeyIds();
@Nullable
@SuppressWarnings("mutable")
abstract byte[] getDetachedSignature();
@Nullable
abstract String getSenderAddress();
public abstract Builder toBuilder();
public static Builder builder() {
return new AutoValue_PgpDecryptVerifyInputParcel.Builder()
.setAllowSymmetricDecryption(false)
.setDecryptMetadataOnly(false);
}
public PgpDecryptVerifyInputParcel(Uri inputUri, Uri outputUri) {
mInputUri = inputUri;
mOutputUri = outputUri;
}
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setInputBytes(byte[] inputBytes);
public abstract Builder setInputUri(Uri inputUri);
public abstract Builder setOutputUri(Uri outputUri);
public PgpDecryptVerifyInputParcel(byte[] inputBytes) {
mInputBytes = inputBytes;
}
public abstract Builder setAllowSymmetricDecryption(boolean allowSymmetricDecryption);
public abstract Builder setDecryptMetadataOnly(boolean decryptMetadataOnly);
public abstract Builder setDetachedSignature(byte[] detachedSignature);
public abstract Builder setSenderAddress(String senderAddress);
PgpDecryptVerifyInputParcel(Parcel source) {
// we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
mInputUri = source.readParcelable(getClass().getClassLoader());
mOutputUri = source.readParcelable(getClass().getClassLoader());
mInputBytes = source.createByteArray();
public abstract Builder setAllowedKeyIds(List<Long> allowedKeyIds);
abstract List<Long> getAllowedKeyIds();
mAllowSymmetricDecryption = source.readInt() != 0;
mAllowedKeyIds = (HashSet<Long>) source.readSerializable();
mDecryptMetadataOnly = source.readInt() != 0;
mDetachedSignature = source.createByteArray();
mRequiredSignerFingerprint = source.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mInputUri, 0);
dest.writeParcelable(mOutputUri, 0);
dest.writeByteArray(mInputBytes);
dest.writeInt(mAllowSymmetricDecryption ? 1 : 0);
dest.writeSerializable(mAllowedKeyIds);
dest.writeInt(mDecryptMetadataOnly ? 1 : 0);
dest.writeByteArray(mDetachedSignature);
dest.writeString(mRequiredSignerFingerprint);
}
byte[] getInputBytes() {
return mInputBytes;
}
public PgpDecryptVerifyInputParcel setInputUri(Uri uri) {
mInputUri = uri;
return this;
}
Uri getInputUri() {
return mInputUri;
}
public PgpDecryptVerifyInputParcel setOutputUri(Uri uri) {
mOutputUri = uri;
return this;
}
Uri getOutputUri() {
return mOutputUri;
}
boolean isAllowSymmetricDecryption() {
return mAllowSymmetricDecryption;
}
public PgpDecryptVerifyInputParcel setAllowSymmetricDecryption(boolean allowSymmetricDecryption) {
mAllowSymmetricDecryption = allowSymmetricDecryption;
return this;
}
HashSet<Long> getAllowedKeyIds() {
return mAllowedKeyIds;
}
public PgpDecryptVerifyInputParcel setAllowedKeyIds(HashSet<Long> allowedKeyIds) {
mAllowedKeyIds = allowedKeyIds;
return this;
}
boolean isDecryptMetadataOnly() {
return mDecryptMetadataOnly;
}
public PgpDecryptVerifyInputParcel setDecryptMetadataOnly(boolean decryptMetadataOnly) {
mDecryptMetadataOnly = decryptMetadataOnly;
return this;
}
byte[] getDetachedSignature() {
return mDetachedSignature;
}
public PgpDecryptVerifyInputParcel setDetachedSignature(byte[] detachedSignature) {
mDetachedSignature = detachedSignature;
return this;
}
public PgpDecryptVerifyInputParcel setSenderAddress(String senderAddress) {
mSenderAddress = senderAddress;
return this;
}
public String getSenderAddress() {
return mSenderAddress;
}
String getRequiredSignerFingerprint() {
return mRequiredSignerFingerprint;
}
public PgpDecryptVerifyInputParcel setRequiredSignerFingerprint(String requiredSignerFingerprint) {
mRequiredSignerFingerprint = requiredSignerFingerprint;
return this;
}
public static final Creator<PgpDecryptVerifyInputParcel> CREATOR = new Creator<PgpDecryptVerifyInputParcel>() {
public PgpDecryptVerifyInputParcel createFromParcel(final Parcel source) {
return new PgpDecryptVerifyInputParcel(source);
abstract PgpDecryptVerifyInputParcel autoBuild();
public PgpDecryptVerifyInputParcel build() {
List<Long> allowedKeyIds = getAllowedKeyIds();
if (allowedKeyIds != null) {
setAllowedKeyIds(Collections.unmodifiableList(allowedKeyIds));
}
return autoBuild();
}
public PgpDecryptVerifyInputParcel[] newArray(final int size) {
return new PgpDecryptVerifyInputParcel[size];
}
};
}
}

View File

@@ -69,10 +69,9 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
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.EncryptionAlgorithmProblem;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
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;
@@ -321,6 +320,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
decryptionResultBuilder.setEncrypted(true);
if (esResult.sessionKey != null && esResult.decryptedSessionKey != null) {
decryptionResultBuilder.setSessionKey(esResult.sessionKey, esResult.decryptedSessionKey);
cryptoInput = cryptoInput.withCryptoData(esResult.sessionKey, esResult.decryptedSessionKey);
}
if (esResult.encryptionKeySecurityProblem != null) {
@@ -361,10 +361,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1);
PGPCompressedData compressedData = (PGPCompressedData) dataChunk;
JcaSkipMarkerPGPObjectFactory fact = new JcaSkipMarkerPGPObjectFactory(compressedData.getDataStream());
dataChunk = fact.nextObject();
plainFact = fact;
plainFact = new JcaSkipMarkerPGPObjectFactory(compressedData.getDataStream());
dataChunk = plainFact.nextObject();
}
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(
@@ -378,10 +376,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
dataChunk = plainFact.nextObject();
}
OpenPgpMetadata metadata;
if ( ! (dataChunk instanceof PGPLiteralData)) {
if (!(dataChunk instanceof PGPLiteralData)) {
log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
@@ -422,6 +417,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1,
new Date(literalData.getModificationTime().getTime()).toString());
OpenPgpMetadata metadata;
// return here if we want to decrypt the metadata only
if (input.isDecryptMetadataOnly()) {
@@ -820,7 +817,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
result.encryptedData = encryptedDataAsymmetric;
Map<ByteBuffer, byte[]> cachedSessionKeys = decryptorFactory.getCachedSessionKeys();
cryptoInput.addCryptoData(cachedSessionKeys);
if (cachedSessionKeys.size() >= 1) {
Entry<ByteBuffer, byte[]> entry = cachedSessionKeys.entrySet().iterator().next();
result.sessionKey = entry.getKey().array();

View File

@@ -32,6 +32,7 @@ import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -79,8 +80,10 @@ import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
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.Builder;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenKeyToCardOperationsBuilder;
@@ -166,17 +169,17 @@ public class PgpKeyOperation {
try {
// Some safety checks
if (add.mAlgorithm == Algorithm.ECDH || add.mAlgorithm == Algorithm.ECDSA) {
if (add.mCurve == null) {
if (add.getAlgorithm() == Algorithm.ECDH || add.getAlgorithm() == Algorithm.ECDSA) {
if (add.getCurve() == null) {
log.add(LogType.MSG_CR_ERROR_NO_CURVE, indent);
return null;
}
} else {
if (add.mKeySize == null) {
if (add.getKeySize() == null) {
log.add(LogType.MSG_CR_ERROR_NO_KEYSIZE, indent);
return null;
}
if (add.mKeySize < 2048) {
if (add.getKeySize() < 2048) {
log.add(LogType.MSG_CR_ERROR_KEYSIZE_2048, indent);
return null;
}
@@ -185,27 +188,27 @@ public class PgpKeyOperation {
int algorithm;
KeyPairGenerator keyGen;
switch (add.mAlgorithm) {
switch (add.getAlgorithm()) {
case DSA: {
if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
if ((add.getFlags() & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
log.add(LogType.MSG_CR_ERROR_FLAGS_DSA, indent);
return null;
}
progress(R.string.progress_generating_dsa, 30);
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(add.mKeySize, new SecureRandom());
keyGen.initialize(add.getKeySize(), new SecureRandom());
algorithm = PGPPublicKey.DSA;
break;
}
case ELGAMAL: {
if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
if ((add.getFlags() & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
log.add(LogType.MSG_CR_ERROR_FLAGS_ELGAMAL, indent);
return null;
}
progress(R.string.progress_generating_elgamal, 30);
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(add.mKeySize);
BigInteger p = Primes.getBestPrime(add.getKeySize());
BigInteger g = new BigInteger("2");
ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
@@ -218,19 +221,19 @@ public class PgpKeyOperation {
case RSA: {
progress(R.string.progress_generating_rsa, 30);
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(add.mKeySize, new SecureRandom());
keyGen.initialize(add.getKeySize(), new SecureRandom());
algorithm = PGPPublicKey.RSA_GENERAL;
break;
}
case ECDSA: {
if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
if ((add.getFlags() & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
log.add(LogType.MSG_CR_ERROR_FLAGS_ECDSA, indent);
return null;
}
progress(R.string.progress_generating_ecdsa, 30);
ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve);
ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.getCurve());
keyGen = KeyPairGenerator.getInstance("ECDSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(ecParamSpec, new SecureRandom());
@@ -240,12 +243,12 @@ public class PgpKeyOperation {
case ECDH: {
// make sure there are no sign or certify flags set
if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
if ((add.getFlags() & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
log.add(LogType.MSG_CR_ERROR_FLAGS_ECDH, indent);
return null;
}
progress(R.string.progress_generating_ecdh, 30);
ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve);
ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.getCurve());
keyGen = KeyPairGenerator.getInstance("ECDH", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(ecParamSpec, new SecureRandom());
@@ -285,23 +288,23 @@ public class PgpKeyOperation {
progress(R.string.progress_building_key, 0);
indent += 1;
if (saveParcel.mAddSubKeys.isEmpty()) {
if (saveParcel.getAddSubKeys().isEmpty()) {
log.add(LogType.MSG_CR_ERROR_NO_MASTER, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (saveParcel.mAddUserIds.isEmpty()) {
if (saveParcel.getAddUserIds().isEmpty()) {
log.add(LogType.MSG_CR_ERROR_NO_USER_ID, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
SubkeyAdd add = saveParcel.mAddSubKeys.remove(0);
if ((add.mFlags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
SubkeyAdd certificationKey = saveParcel.getAddSubKeys().get(0);
if ((certificationKey.getFlags() & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
log.add(LogType.MSG_CR_ERROR_NO_CERTIFY, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (add.mExpiry == null) {
if (certificationKey.getExpiry() == null) {
log.add(LogType.MSG_CR_ERROR_NULL_EXPIRY, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
@@ -309,7 +312,7 @@ public class PgpKeyOperation {
Date creationTime = new Date();
subProgressPush(10, 30);
PGPKeyPair keyPair = createKey(add, creationTime, log, indent);
PGPKeyPair keyPair = createKey(certificationKey, creationTime, log, indent);
subProgressPop();
// return null if this failed (an error will already have been logged by createKey)
@@ -335,9 +338,14 @@ public class PgpKeyOperation {
PGPSecretKeyRing sKR = new PGPSecretKeyRing(
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
// Remove certification key from remaining SaveKeyringParcel
Builder builder = SaveKeyringParcel.buildUpon(saveParcel);
builder.getMutableAddSubKeys().remove(certificationKey);
saveParcel = builder.build();
subProgressPush(50, 100);
CryptoInputParcel cryptoInput = new CryptoInputParcel(creationTime, new Passphrase(""));
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, cryptoInput, saveParcel, log, indent);
CryptoInputParcel cryptoInput = CryptoInputParcel.createCryptoInputParcel(creationTime, new Passphrase(""));
return internal(sKR, masterSecretKey, certificationKey.getFlags(), certificationKey.getExpiry(), cryptoInput, saveParcel, log, indent);
} catch (PGPException e) {
log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
@@ -392,7 +400,7 @@ public class PgpKeyOperation {
progress(R.string.progress_building_key, 0);
// Make sure this is called with a proper SaveKeyringParcel
if (saveParcel.mMasterKeyId == null || saveParcel.mMasterKeyId != wsKR.getMasterKeyId()) {
if (saveParcel.getMasterKeyId() == null || saveParcel.getMasterKeyId() != wsKR.getMasterKeyId()) {
log.add(LogType.MSG_MF_ERROR_KEYID, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
@@ -402,75 +410,29 @@ public class PgpKeyOperation {
PGPSecretKey masterSecretKey = sKR.getSecretKey();
// Make sure the fingerprint matches
if (saveParcel.mFingerprint == null || !Arrays.equals(saveParcel.mFingerprint,
if (saveParcel.getFingerprint() == null || !Arrays.equals(saveParcel.getFingerprint(),
masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogType.MSG_MF_ERROR_FINGERPRINT, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (saveParcel.isEmpty()) {
if (isParcelEmpty(saveParcel)) {
log.add(LogType.MSG_MF_ERROR_NOOP, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// Ensure we don't have multiple keys for the same slot.
boolean hasSign = false;
boolean hasEncrypt = false;
boolean hasAuth = false;
for(SaveKeyringParcel.SubkeyChange change : saveParcel.mChangeSubKeys) {
if (change.mMoveKeyToSecurityToken) {
// If this is a moveKeyToSecurityToken operation, see if it was completed: look for a hash
// matching the given subkey ID in cryptoData.
byte[] subKeyId = new byte[8];
ByteBuffer buf = ByteBuffer.wrap(subKeyId);
buf.putLong(change.mKeyId).rewind();
saveParcel = parseSecurityTokenSerialNumberIntoSubkeyChanges(cryptoInput, saveParcel);
byte[] serialNumber = cryptoInput.getCryptoData().get(buf);
if (serialNumber != null) {
change.mMoveKeyToSecurityToken = false;
change.mSecurityTokenSerialNo = serialNumber;
}
}
if (change.mMoveKeyToSecurityToken) {
// Pending moveKeyToSecurityToken operation. Need to make sure that we don't have multiple
// subkeys pending for the same slot.
CanonicalizedSecretKey wsK = wsKR.getSecretKey(change.mKeyId);
if ((wsK.canSign() || wsK.canCertify())) {
if (hasSign) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} else {
hasSign = true;
}
} else if ((wsK.canEncrypt())) {
if (hasEncrypt) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} else {
hasEncrypt = true;
}
} else if ((wsK.canAuthenticate())) {
if (hasAuth) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} else {
hasAuth = true;
}
} else {
log.add(LogType.MSG_MF_ERROR_INVALID_FLAGS_FOR_KEYTOCARD, indent + 1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
}
if (!checkCapabilitiesAreUnique(wsKR, saveParcel, log, indent)) {
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (isDummy(masterSecretKey) && ! saveParcel.isRestrictedOnly()) {
if (isDummy(masterSecretKey) && ! isParcelRestrictedOnly(saveParcel)) {
log.add(LogType.MSG_EK_ERROR_DUMMY, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (isDummy(masterSecretKey) || saveParcel.isRestrictedOnly()) {
if (isDummy(masterSecretKey) || isParcelRestrictedOnly(saveParcel)) {
log.add(LogType.MSG_MF_RESTRICTED_MODE, indent);
return internalRestricted(sKR, saveParcel, log, indent + 1);
}
@@ -494,6 +456,70 @@ public class PgpKeyOperation {
}
private SaveKeyringParcel parseSecurityTokenSerialNumberIntoSubkeyChanges(CryptoInputParcel cryptoInput,
SaveKeyringParcel saveParcel) {
SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildUpon(saveParcel);
for (SubkeyChange change : saveParcel.getChangeSubKeys()) {
if (change.getMoveKeyToSecurityToken()) {
// If this is a moveKeyToSecurityToken operation, see if it was completed: look for a hash
// matching the given subkey ID in cryptoData.
byte[] subKeyId = new byte[8];
ByteBuffer buf = ByteBuffer.wrap(subKeyId);
buf.putLong(change.getSubKeyId()).rewind();
byte[] serialNumber = cryptoInput.getCryptoData().get(buf);
if (serialNumber != null) {
builder.addOrReplaceSubkeyChange(
SubkeyChange.createSecurityTokenSerialNo(change.getSubKeyId(), serialNumber));
}
}
}
saveParcel = builder.build();
return saveParcel;
}
private boolean checkCapabilitiesAreUnique(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
OperationLog log, int indent) {
boolean hasSign = false;
boolean hasEncrypt = false;
boolean hasAuth = false;
for (SubkeyChange change : saveParcel.getChangeSubKeys()) {
if (change.getMoveKeyToSecurityToken()) {
// Pending moveKeyToSecurityToken operation. Need to make sure that we don't have multiple
// subkeys pending for the same slot.
CanonicalizedSecretKey wsK = wsKR.getSecretKey(change.getSubKeyId());
if ((wsK.canSign() || wsK.canCertify())) {
if (hasSign) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return false;
} else {
hasSign = true;
}
} else if ((wsK.canEncrypt())) {
if (hasEncrypt) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return false;
} else {
hasEncrypt = true;
}
} else if ((wsK.canAuthenticate())) {
if (hasAuth) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return false;
} else {
hasAuth = true;
}
} else {
log.add(LogType.MSG_MF_ERROR_INVALID_FLAGS_FOR_KEYTOCARD, indent + 1);
return false;
}
}
}
return true;
}
private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
int masterKeyFlags, long masterKeyExpiry,
CryptoInputParcel cryptoInput,
@@ -547,10 +573,11 @@ public class PgpKeyOperation {
// 2a. Add certificates for new user ids
subProgressPush(15, 23);
for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
String changePrimaryUserId = saveParcel.getChangePrimaryUserId();
for (int i = 0; i < saveParcel.getAddUserIds().size(); i++) {
progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size()));
String userId = saveParcel.mAddUserIds.get(i);
progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.getAddUserIds().size()));
String userId = saveParcel.getAddUserIds().get(i);
log.add(LogType.MSG_MF_UID_ADD, indent, userId);
if ("".equals(userId)) {
@@ -581,8 +608,8 @@ public class PgpKeyOperation {
}
// if it's supposed to be primary, we can do that here as well
boolean isPrimary = saveParcel.mChangePrimaryUserId != null
&& userId.equals(saveParcel.mChangePrimaryUserId);
boolean isPrimary = changePrimaryUserId != null
&& userId.equals(changePrimaryUserId);
// generate and add new certificate
try {
PGPSignature cert = generateUserIdSignature(
@@ -599,10 +626,10 @@ public class PgpKeyOperation {
// 2b. Add certificates for new user ids
subProgressPush(23, 32);
for (int i = 0; i < saveParcel.mAddUserAttribute.size(); i++) {
progress(R.string.progress_modify_adduat, (i - 1) * (100 / saveParcel.mAddUserAttribute.size()));
WrappedUserAttribute attribute = saveParcel.mAddUserAttribute.get(i);
List<WrappedUserAttribute> addUserAttributes = saveParcel.getAddUserAttribute();
for (int i = 0; i < addUserAttributes.size(); i++) {
progress(R.string.progress_modify_adduat, (i - 1) * (100 / addUserAttributes.size()));
WrappedUserAttribute attribute = addUserAttributes.get(i);
switch (attribute.getType()) {
// the 'none' type must not succeed
@@ -635,10 +662,10 @@ public class PgpKeyOperation {
// 2c. Add revocations for revoked user ids
subProgressPush(32, 40);
for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / saveParcel.mRevokeUserIds.size()));
String userId = saveParcel.mRevokeUserIds.get(i);
List<String> revokeUserIds = saveParcel.getRevokeUserIds();
for (int i = 0, j = revokeUserIds.size(); i < j; i++) {
progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / revokeUserIds.size()));
String userId = revokeUserIds.get(i);
log.add(LogType.MSG_MF_UID_REVOKE, indent, userId);
// Make sure the user id exists (yes these are 10 LoC in Java!)
@@ -670,12 +697,12 @@ public class PgpKeyOperation {
subProgressPop();
// 3. If primary user id changed, generate new certificates for both old and new
if (saveParcel.mChangePrimaryUserId != null) {
if (changePrimaryUserId != null) {
progress(R.string.progress_modify_primaryuid, 40);
// keep track if we actually changed one
boolean ok = false;
log.add(LogType.MSG_MF_UID_PRIMARY, indent, saveParcel.mChangePrimaryUserId);
log.add(LogType.MSG_MF_UID_PRIMARY, indent, changePrimaryUserId);
indent += 1;
// we work on the modifiedPublicKey here, to respect new or newly revoked uids
@@ -716,7 +743,7 @@ public class PgpKeyOperation {
// we definitely should not update certifications of revoked keys, so just leave it.
if (isRevoked) {
// revoked user ids cannot be primary!
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
if (userId.equals(changePrimaryUserId)) {
log.add(LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
@@ -727,7 +754,7 @@ public class PgpKeyOperation {
if (currentCert.getHashedSubPackets() != null
&& currentCert.getHashedSubPackets().isPrimaryUserID()) {
// if it's the one we want, just leave it as is
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
if (userId.equals(changePrimaryUserId)) {
ok = true;
continue;
}
@@ -753,7 +780,7 @@ public class PgpKeyOperation {
// if we are here, this is not currently a primary user id
// if it should be
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
if (userId.equals(changePrimaryUserId)) {
// add shiny new primary user id certificate
log.add(LogType.MSG_MF_PRIMARY_NEW, indent);
modifiedPublicKey = PGPPublicKey.removeCertification(
@@ -801,67 +828,68 @@ public class PgpKeyOperation {
// 4a. For each subkey change, generate new subkey binding certificate
subProgressPush(50, 60);
for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) {
List<SubkeyChange> changeSubKeys = saveParcel.getChangeSubKeys();
for (int i = 0, j = changeSubKeys.size(); i < j; i++) {
progress(R.string.progress_modify_subkeychange, (i-1) * (100 / saveParcel.mChangeSubKeys.size()));
SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i);
progress(R.string.progress_modify_subkeychange, (i-1) * (100 / changeSubKeys.size()));
SaveKeyringParcel.SubkeyChange change = changeSubKeys.get(i);
log.add(LogType.MSG_MF_SUBKEY_CHANGE,
indent, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
indent, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
PGPSecretKey sKey = sKR.getSecretKey(change.getSubKeyId());
if (sKey == null) {
log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (change.mDummyStrip) {
if (change.getDummyStrip()) {
// IT'S DANGEROUS~
// no really, it is. this operation irrevocably removes the private key data from the key
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
} else if (change.mMoveKeyToSecurityToken) {
} else if (change.getMoveKeyToSecurityToken()) {
if (checkSecurityTokenCompatibility(sKey, log, indent + 1)) {
log.add(LogType.MSG_MF_KEYTOCARD_START, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
nfcKeyToCardOps.addSubkey(change.mKeyId);
KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
nfcKeyToCardOps.addSubkey(change.getSubKeyId());
} else {
// Appropriate log message already set by checkSecurityTokenCompatibility
return new PgpEditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
} else if (change.mSecurityTokenSerialNo != null) {
} else if (change.getSecurityTokenSerialNo() != null) {
// NOTE: Does this code get executed? Or always handled in internalRestricted?
if (change.mSecurityTokenSerialNo.length != 16) {
if (change.getSecurityTokenSerialNo().length != 16) {
log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(change.mKeyId),
Hex.toHexString(change.mSecurityTokenSerialNo, 8, 6));
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mSecurityTokenSerialNo);
KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()),
Hex.toHexString(change.getSecurityTokenSerialNo(), 8, 6));
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.getSecurityTokenSerialNo());
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
}
// This doesn't concern us any further
if (!change.mRecertify && (change.mExpiry == null && change.mFlags == null)) {
if (!change.getRecertify() && (change.getExpiry() == null && change.getFlags() == null)) {
continue;
}
// expiry must not be in the past
if (change.mExpiry != null && change.mExpiry != 0 &&
new Date(change.mExpiry*1000).before(new Date())) {
if (change.getExpiry() != null && change.getExpiry() != 0 &&
new Date(change.getExpiry() * 1000).before(new Date())) {
log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// if this is the master key, update uid certificates instead
if (change.mKeyId == masterPublicKey.getKeyID()) {
int flags = change.mFlags == null ? masterKeyFlags : change.mFlags;
long expiry = change.mExpiry == null ? masterKeyExpiry : change.mExpiry;
if (change.getSubKeyId() == masterPublicKey.getKeyID()) {
int flags = change.getFlags() == null ? masterKeyFlags : change.getFlags();
long expiry = change.getExpiry() == null ? masterKeyExpiry : change.getExpiry();
if ((flags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
log.add(LogType.MSG_MF_ERROR_NO_CERTIFY, indent + 1);
@@ -886,22 +914,22 @@ public class PgpKeyOperation {
PGPPublicKey pKey = sKey.getPublicKey();
// keep old flags, or replace with new ones
int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
int flags = change.getFlags() == null ? readKeyFlags(pKey) : change.getFlags();
long expiry;
if (change.mExpiry == null) {
if (change.getExpiry() == null) {
long valid = pKey.getValidSeconds();
expiry = valid == 0
? 0
: pKey.getCreationTime().getTime() / 1000 + pKey.getValidSeconds();
} else {
expiry = change.mExpiry;
expiry = change.getExpiry();
}
// drop all old signatures, they will be superseded by the new one
//noinspection unchecked
for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
// special case: if there is a revocation, don't use expiry from before
if ( (change.mExpiry == null || change.mExpiry == 0L)
if ( (change.getExpiry() == null || change.getExpiry() == 0L)
&& sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
expiry = 0;
}
@@ -917,7 +945,7 @@ public class PgpKeyOperation {
// super special case: subkey is allowed to sign, but isn't available
if (subPrivateKey == null) {
log.add(LogType.MSG_MF_ERROR_SUB_STRIPPED,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
} else {
@@ -942,10 +970,10 @@ public class PgpKeyOperation {
// 4b. For each subkey revocation, generate new subkey revocation certificate
subProgressPush(60, 65);
for (int i = 0; i < saveParcel.mRevokeSubKeys.size(); i++) {
progress(R.string.progress_modify_subkeyrevoke, (i-1) * (100 / saveParcel.mRevokeSubKeys.size()));
long revocation = saveParcel.mRevokeSubKeys.get(i);
List<Long> revokeSubKeys = saveParcel.getRevokeSubKeys();
for (int i = 0, j = revokeSubKeys.size(); i < j; i++) {
progress(R.string.progress_modify_subkeyrevoke, (i-1) * (100 / revokeSubKeys.size()));
long revocation = revokeSubKeys.get(i);
log.add(LogType.MSG_MF_SUBKEY_REVOKE,
indent, KeyFormattingUtils.convertKeyIdToHex(revocation));
@@ -974,38 +1002,38 @@ public class PgpKeyOperation {
// 5. Generate and add new subkeys
subProgressPush(70, 90);
for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) {
List<SubkeyAdd> addSubKeys = saveParcel.getAddSubKeys();
for (int i = 0, j = addSubKeys.size(); i < j; i++) {
// Check if we were cancelled - again. This operation is expensive so we do it each loop.
if (checkCancelled()) {
log.add(LogType.MSG_OPERATION_CANCELLED, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
}
progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i);
progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / addSubKeys.size()));
SaveKeyringParcel.SubkeyAdd add = addSubKeys.get(i);
log.add(LogType.MSG_MF_SUBKEY_NEW, indent,
KeyFormattingUtils.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) );
KeyFormattingUtils.getAlgorithmInfo(add.getAlgorithm(), add.getKeySize(), add.getCurve()) );
if (isDivertToCard(masterSecretKey)) {
log.add(LogType.MSG_MF_ERROR_DIVERT_NEWSUB, indent +1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (add.mExpiry == null) {
if (add.getExpiry() == null) {
log.add(LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (add.mExpiry > 0L && new Date(add.mExpiry*1000).before(new Date())) {
if (add.getExpiry() > 0L && new Date(add.getExpiry() * 1000).before(new Date())) {
log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY, indent +1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// generate a new secret key (privkey only for now)
subProgressPush(
(i-1) * (100 / saveParcel.mAddSubKeys.size()),
i * (100 / saveParcel.mAddSubKeys.size())
(i-1) * (100 / addSubKeys.size()),
i * (100 / addSubKeys.size())
);
PGPKeyPair keyPair = createKey(add, cryptoInput.getSignatureTime(), log, indent);
subProgressPop();
@@ -1022,7 +1050,7 @@ public class PgpKeyOperation {
cryptoInput.getSignatureTime(),
masterPublicKey, masterPrivateKey,
getSignatureGenerator(pKey, cryptoInput, false), keyPair.getPrivateKey(), pKey,
add.mFlags, add.mExpiry);
add.getFlags(), add.getExpiry());
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
} catch (NfcInteractionNeeded e) {
nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
@@ -1058,13 +1086,13 @@ public class PgpKeyOperation {
}
// 6. If requested, change passphrase
if (saveParcel.getChangeUnlockParcel() != null) {
if (saveParcel.getNewUnlock() != null) {
progress(R.string.progress_modify_passphrase, 90);
log.add(LogType.MSG_MF_PASSPHRASE, indent);
indent += 1;
sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(),
saveParcel.getChangeUnlockParcel().mNewPassphrase, log, indent);
saveParcel.getNewUnlock().getNewPassphrase(), log, indent);
if (sKR == null) {
// The error has been logged above, just return a bad state
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -1074,21 +1102,21 @@ public class PgpKeyOperation {
}
// 7. if requested, change PIN and/or Admin PIN on security token
if (saveParcel.mSecurityTokenPin != null) {
if (saveParcel.getSecurityTokenPin() != null) {
progress(R.string.progress_modify_pin, 90);
log.add(LogType.MSG_MF_PIN, indent);
indent += 1;
nfcKeyToCardOps.setPin(saveParcel.mSecurityTokenPin);
nfcKeyToCardOps.setPin(saveParcel.getSecurityTokenPin());
indent -= 1;
}
if (saveParcel.mSecurityTokenAdminPin != null) {
if (saveParcel.getSecurityTokenAdminPin() != null) {
progress(R.string.progress_modify_admin_pin, 90);
log.add(LogType.MSG_MF_ADMIN_PIN, indent);
indent += 1;
nfcKeyToCardOps.setAdminPin(saveParcel.mSecurityTokenAdminPin);
nfcKeyToCardOps.setAdminPin(saveParcel.getSecurityTokenAdminPin());
indent -= 1;
}
@@ -1139,7 +1167,7 @@ public class PgpKeyOperation {
progress(R.string.progress_modify, 0);
// Make sure the saveParcel includes only operations available without passphrase!
if (!saveParcel.isRestrictedOnly()) {
if (!isParcelRestrictedOnly(saveParcel)) {
log.add(LogType.MSG_MF_ERROR_RESTRICTED, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
@@ -1153,36 +1181,36 @@ public class PgpKeyOperation {
// The only operation we can do here:
// 4a. Strip secret keys, or change their protection mode (stripped/divert-to-card)
subProgressPush(50, 60);
for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) {
progress(R.string.progress_modify_subkeychange, (i - 1) * (100 / saveParcel.mChangeSubKeys.size()));
SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i);
List<SubkeyChange> changeSubKeys = saveParcel.getChangeSubKeys();
for (int i = 0, j = changeSubKeys.size(); i < j; i++) {
progress(R.string.progress_modify_subkeychange, (i - 1) * (100 / changeSubKeys.size()));
SaveKeyringParcel.SubkeyChange change = changeSubKeys.get(i);
log.add(LogType.MSG_MF_SUBKEY_CHANGE,
indent, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
indent, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
PGPSecretKey sKey = sKR.getSecretKey(change.getSubKeyId());
if (sKey == null) {
log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (change.mDummyStrip || change.mSecurityTokenSerialNo != null) {
if (change.getDummyStrip() || change.getSecurityTokenSerialNo() != null) {
// IT'S DANGEROUS~
// no really, it is. this operation irrevocably removes the private key data from the key
if (change.mDummyStrip) {
if (change.getDummyStrip()) {
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
} else {
// the serial number must be 16 bytes in length
if (change.mSecurityTokenSerialNo.length != 16) {
if (change.getSecurityTokenSerialNo().length != 16) {
log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(change.mKeyId),
Hex.toHexString(change.mSecurityTokenSerialNo, 8, 6));
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mSecurityTokenSerialNo);
KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()),
Hex.toHexString(change.getSecurityTokenSerialNo(), 8, 6));
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.getSecurityTokenSerialNo());
}
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
}
@@ -1204,7 +1232,8 @@ public class PgpKeyOperation {
OperationLog log = new OperationLog();
int indent = 0;
if (changeUnlockParcel.mMasterKeyId == null || changeUnlockParcel.mMasterKeyId != wsKR.getMasterKeyId()) {
Long masterKeyId = changeUnlockParcel.getMasterKeyId();
if (masterKeyId == null || masterKeyId != wsKR.getMasterKeyId()) {
log.add(LogType.MSG_MF_ERROR_KEYID, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
@@ -1219,7 +1248,7 @@ public class PgpKeyOperation {
PGPSecretKey masterSecretKey = sKR.getSecretKey();
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
// Make sure the fingerprint matches
if (changeUnlockParcel.mFingerprint == null || !Arrays.equals(changeUnlockParcel.mFingerprint,
if (changeUnlockParcel.getFingerprint()== null || !Arrays.equals(changeUnlockParcel.getFingerprint(),
masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogType.MSG_MF_ERROR_FINGERPRINT, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -1245,7 +1274,7 @@ public class PgpKeyOperation {
try {
sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(),
changeUnlockParcel.mNewPassphrase, log, indent);
changeUnlockParcel.getNewPassphrase(), log, indent);
if (sKR == null) {
// The error has been logged above, just return a bad state
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -1697,4 +1726,31 @@ public class PgpKeyOperation {
return true;
}
/** Returns true iff this parcel does not contain any operations which require a passphrase. */
private static boolean isParcelRestrictedOnly(SaveKeyringParcel saveKeyringParcel) {
if (saveKeyringParcel.getNewUnlock() != null
|| !saveKeyringParcel.getAddUserIds().isEmpty()
|| !saveKeyringParcel.getAddUserAttribute().isEmpty()
|| !saveKeyringParcel.getAddSubKeys().isEmpty()
|| saveKeyringParcel.getChangePrimaryUserId() != null
|| !saveKeyringParcel.getRevokeUserIds().isEmpty()
|| !saveKeyringParcel.getRevokeSubKeys().isEmpty()) {
return false;
}
for (SubkeyChange change : saveKeyringParcel.getChangeSubKeys()) {
if (change.getRecertify() || change.getFlags() != null || change.getExpiry() != null
|| change.getMoveKeyToSecurityToken()) {
return false;
}
}
return true;
}
private static boolean isParcelEmpty(SaveKeyringParcel saveKeyringParcel) {
return isParcelRestrictedOnly(saveKeyringParcel) && saveKeyringParcel.getChangeSubKeys().isEmpty();
}
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 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,226 +18,91 @@
package org.sufficientlysecure.keychain.pgp;
import android.os.Parcel;
import android.os.Parcelable;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainHashAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags;
import org.sufficientlysecure.keychain.util.Passphrase;
@AutoValue
public abstract class PgpSignEncryptData implements Parcelable {
@Nullable
public abstract String getCharset();
abstract long getAdditionalEncryptId();
@Nullable
public abstract Long getSignatureSubKeyId();
public abstract long getSignatureMasterKeyId();
@Nullable
public abstract Passphrase getSymmetricPassphrase();
@Nullable
@SuppressWarnings("mutable")
public abstract long[] getEncryptionMasterKeyIds();
@Nullable
public abstract List<Long> getAllowedSigningKeyIds();
@Nullable
public abstract String getVersionHeader();
public class PgpSignEncryptData implements Parcelable {
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 abstract int getCompressionAlgorithm();
public abstract int getSignatureHashAlgorithm();
public abstract int getSymmetricEncryptionAlgorithm();
public PgpSignEncryptData(){
public abstract boolean isEnableAsciiArmorOutput();
public abstract boolean isCleartextSignature();
public abstract boolean isDetachedSignature();
public abstract boolean isAddBackupHeader();
public abstract boolean isHiddenRecipients();
public static Builder builder() {
return new AutoValue_PgpSignEncryptData.Builder()
.setSignatureMasterKeyId(Constants.key.none)
.setAdditionalEncryptId(Constants.key.none)
.setEnableAsciiArmorOutput(false)
.setCleartextSignature(false)
.setDetachedSignature(false)
.setAddBackupHeader(false)
.setHiddenRecipients(false)
.setCompressionAlgorithm(OpenKeychainCompressionAlgorithmTags.USE_DEFAULT)
.setSignatureHashAlgorithm(OpenKeychainHashAlgorithmTags.USE_DEFAULT)
.setSymmetricEncryptionAlgorithm(OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
}
private PgpSignEncryptData(Parcel source) {
ClassLoader loader = getClass().getClassLoader();
@AutoValue.Builder
public abstract static class Builder {
public abstract PgpSignEncryptData build();
mVersionHeader = source.readString();
mEnableAsciiArmorOutput = source.readInt() == 1;
mCompressionAlgorithm = source.readInt();
mEncryptionMasterKeyIds = source.createLongArray();
mSymmetricPassphrase = source.readParcelable(loader);
mSymmetricEncryptionAlgorithm = source.readInt();
mSignatureMasterKeyId = source.readLong();
mSignatureSubKeyId = source.readInt() == 1 ? source.readLong() : null;
mSignatureHashAlgorithm = source.readInt();
mAdditionalEncryptId = source.readLong();
mCharset = source.readString();
mCleartextSignature = source.readInt() == 1;
mDetachedSignature = source.readInt() == 1;
mHiddenRecipients = source.readInt() == 1;
mAddBackupHeader = source.readInt() == 1;
}
public abstract Builder setCharset(String charset);
public abstract Builder setAdditionalEncryptId(long additionalEncryptId);
public abstract Builder setSignatureSubKeyId(Long signatureSubKeyId);
public abstract Builder setSignatureMasterKeyId(long signatureMasterKeyId);
public abstract Builder setSymmetricPassphrase(Passphrase symmetricPassphrase);
public abstract Builder setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds);
public abstract Builder setVersionHeader(String versionHeader);
@Override
public int describeContents() {
return 0;
}
public abstract Builder setCompressionAlgorithm(int compressionAlgorithm);
public abstract Builder setSignatureHashAlgorithm(int signatureHashAlgorithm);
public abstract Builder setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm);
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mVersionHeader);
dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
dest.writeInt(mCompressionAlgorithm);
dest.writeLongArray(mEncryptionMasterKeyIds);
dest.writeParcelable(mSymmetricPassphrase, 0);
dest.writeInt(mSymmetricEncryptionAlgorithm);
dest.writeLong(mSignatureMasterKeyId);
if (mSignatureSubKeyId != null) {
dest.writeInt(1);
dest.writeLong(mSignatureSubKeyId);
} else {
dest.writeInt(0);
public abstract Builder setAddBackupHeader(boolean isAddBackupHeader);
public abstract Builder setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput);
public abstract Builder setCleartextSignature(boolean isCleartextSignature);
public abstract Builder setDetachedSignature(boolean isDetachedSignature);
public abstract Builder setHiddenRecipients(boolean isHiddenRecipients);
abstract Builder setAllowedSigningKeyIds(List<Long> allowedSigningKeyIds);
public Builder setAllowedSigningKeyIds(Collection<Long> allowedSigningKeyIds) {
setAllowedSigningKeyIds(Collections.unmodifiableList(new ArrayList<>(allowedSigningKeyIds)));
return this;
}
dest.writeInt(mSignatureHashAlgorithm);
dest.writeLong(mAdditionalEncryptId);
dest.writeString(mCharset);
dest.writeInt(mCleartextSignature ? 1 : 0);
dest.writeInt(mDetachedSignature ? 1 : 0);
dest.writeInt(mHiddenRecipients ? 1 : 0);
dest.writeInt(mAddBackupHeader ? 1 : 0);
}
public String getCharset() {
return mCharset;
}
public void setCharset(String mCharset) {
this.mCharset = mCharset;
}
public long getAdditionalEncryptId() {
return mAdditionalEncryptId;
}
public PgpSignEncryptData setAdditionalEncryptId(long additionalEncryptId) {
mAdditionalEncryptId = additionalEncryptId;
return this;
}
public int getSignatureHashAlgorithm() {
return mSignatureHashAlgorithm;
}
public PgpSignEncryptData setSignatureHashAlgorithm(int signatureHashAlgorithm) {
mSignatureHashAlgorithm = signatureHashAlgorithm;
return this;
}
public Long getSignatureSubKeyId() {
return mSignatureSubKeyId;
}
public PgpSignEncryptData setSignatureSubKeyId(long signatureSubKeyId) {
mSignatureSubKeyId = signatureSubKeyId;
return this;
}
public long getSignatureMasterKeyId() {
return mSignatureMasterKeyId;
}
public PgpSignEncryptData setSignatureMasterKeyId(long signatureMasterKeyId) {
mSignatureMasterKeyId = signatureMasterKeyId;
return this;
}
public int getSymmetricEncryptionAlgorithm() {
return mSymmetricEncryptionAlgorithm;
}
public PgpSignEncryptData setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
return this;
}
public Passphrase getSymmetricPassphrase() {
return mSymmetricPassphrase;
}
public PgpSignEncryptData setSymmetricPassphrase(Passphrase symmetricPassphrase) {
mSymmetricPassphrase = symmetricPassphrase;
return this;
}
public long[] getEncryptionMasterKeyIds() {
return mEncryptionMasterKeyIds;
}
public PgpSignEncryptData setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {
mEncryptionMasterKeyIds = encryptionMasterKeyIds;
return this;
}
public int getCompressionAlgorithm() {
return mCompressionAlgorithm;
}
public PgpSignEncryptData setCompressionAlgorithm(int compressionAlgorithm) {
mCompressionAlgorithm = compressionAlgorithm;
return this;
}
public boolean isEnableAsciiArmorOutput() {
return mEnableAsciiArmorOutput;
}
public String getVersionHeader() {
return mVersionHeader;
}
public PgpSignEncryptData setVersionHeader(String versionHeader) {
mVersionHeader = versionHeader;
return this;
}
public PgpSignEncryptData setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
mEnableAsciiArmorOutput = enableAsciiArmorOutput;
return this;
}
public PgpSignEncryptData setCleartextSignature(boolean cleartextSignature) {
this.mCleartextSignature = cleartextSignature;
return this;
}
public boolean isCleartextSignature() {
return mCleartextSignature;
}
public PgpSignEncryptData setDetachedSignature(boolean detachedSignature) {
this.mDetachedSignature = detachedSignature;
return this;
}
public boolean isDetachedSignature() {
return mDetachedSignature;
}
public PgpSignEncryptData setHiddenRecipients(boolean hiddenRecipients) {
this.mHiddenRecipients = hiddenRecipients;
return this;
}
public PgpSignEncryptData setAddBackupHeader(boolean addBackupHeader) {
this.mAddBackupHeader = addBackupHeader;
return this;
}
public boolean isAddBackupHeader() {
return mAddBackupHeader;
}
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
public static final Creator<PgpSignEncryptData> CREATOR = new Creator<PgpSignEncryptData>() {
public PgpSignEncryptData createFromParcel(final Parcel source) {
return new PgpSignEncryptData(source);
}
public PgpSignEncryptData[] newArray(final int size) {
return new PgpSignEncryptData[size];
}
};
}

View File

@@ -18,104 +18,33 @@
package org.sufficientlysecure.keychain.pgp;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import java.util.HashSet;
import com.google.auto.value.AutoValue;
public class PgpSignEncryptInputParcel implements Parcelable {
@AutoValue
public abstract class PgpSignEncryptInputParcel implements Parcelable {
public abstract PgpSignEncryptData getData();
@Nullable
public abstract Uri getOutputUri();
@Nullable
public abstract Uri getInputUri();
@Nullable
@SuppressWarnings("mutable")
public abstract byte[] getInputBytes();
private PgpSignEncryptData data;
private Uri mInputUri;
private Uri mOutputUri;
private byte[] mInputBytes;
private HashSet<Long> mAllowedKeyIds;
public PgpSignEncryptInputParcel(PgpSignEncryptData data) {
this.data = data;
public static PgpSignEncryptInputParcel createForBytes(
PgpSignEncryptData signEncryptData, Uri outputUri, byte[] inputBytes) {
return new AutoValue_PgpSignEncryptInputParcel(signEncryptData, outputUri, null, inputBytes);
}
PgpSignEncryptInputParcel(Parcel source) {
mInputUri = source.readParcelable(getClass().getClassLoader());
mOutputUri = source.readParcelable(getClass().getClassLoader());
mInputBytes = source.createByteArray();
data = source.readParcelable(getClass().getClassLoader());
mAllowedKeyIds = (HashSet<Long>) source.readSerializable();
public static PgpSignEncryptInputParcel createForInputUri(
PgpSignEncryptData signEncryptData, Uri outputUri, Uri inputUri) {
return new AutoValue_PgpSignEncryptInputParcel(signEncryptData, outputUri, inputUri, null);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mInputUri, 0);
dest.writeParcelable(mOutputUri, 0);
dest.writeByteArray(mInputBytes);
data.writeToParcel(dest, 0);
dest.writeSerializable(mAllowedKeyIds);
}
public void setInputBytes(byte[] inputBytes) {
this.mInputBytes = inputBytes;
}
byte[] getInputBytes() {
return mInputBytes;
}
public PgpSignEncryptInputParcel setInputUri(Uri uri) {
mInputUri = uri;
return this;
}
Uri getInputUri() {
return mInputUri;
}
public PgpSignEncryptInputParcel setOutputUri(Uri uri) {
mOutputUri = uri;
return this;
}
Uri getOutputUri() {
return mOutputUri;
}
public void setData(PgpSignEncryptData data) {
this.data = data;
}
public PgpSignEncryptData getData() {
return data;
}
HashSet<Long> getAllowedKeyIds() {
return mAllowedKeyIds;
}
public void setAllowedKeyIds(HashSet<Long> allowedKeyIds) {
mAllowedKeyIds = allowedKeyIds;
}
public static final Creator<PgpSignEncryptInputParcel> CREATOR = new Creator<PgpSignEncryptInputParcel>() {
public PgpSignEncryptInputParcel createFromParcel(final Parcel source) {
return new PgpSignEncryptInputParcel(source);
}
public PgpSignEncryptInputParcel[] newArray(final int size) {
return new PgpSignEncryptInputParcel[size];
}
};
}

View File

@@ -19,6 +19,24 @@
package org.sufficientlysecure.keychain.pgp;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
@@ -43,7 +61,11 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainHashAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -56,23 +78,6 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class supports a single, low-level, sign/encrypt operation.
* <p/>
@@ -148,7 +153,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
}
}
PgpSignEncryptResult result = executeInternal(input, cryptoInput, inputData, outStream);
PgpSignEncryptResult result = executeInternal(input.getData(), cryptoInput, inputData, outStream);
if (outStream instanceof ByteArrayOutputStream) {
byte[] outputData = ((ByteArrayOutputStream) outStream).toByteArray();
result.setOutputBytes(outputData);
@@ -158,41 +163,33 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
}
@NonNull
public PgpSignEncryptResult execute(PgpSignEncryptInputParcel input, CryptoInputParcel cryptoInput,
InputData inputData, OutputStream outputStream) {
return executeInternal(input, cryptoInput, inputData, outputStream);
public PgpSignEncryptResult execute(PgpSignEncryptData data, CryptoInputParcel cryptoInput,
InputData inputData, OutputStream outputStream) {
return executeInternal(data, cryptoInput, inputData, outputStream);
}
/**
* Signs and/or encrypts data based on parameters of class
*/
private PgpSignEncryptResult executeInternal(PgpSignEncryptInputParcel input, CryptoInputParcel cryptoInput,
InputData inputData, OutputStream outputStream) {
private PgpSignEncryptResult executeInternal(PgpSignEncryptData data, CryptoInputParcel cryptoInput,
InputData inputData, OutputStream outputStream) {
int indent = 0;
OperationLog log = new OperationLog();
log.add(LogType.MSG_PSE, indent);
indent += 1;
PgpSignEncryptData data = input.getData();
boolean enableSignature = data.getSignatureMasterKeyId() != Constants.key.none;
boolean enableEncryption = ((data.getEncryptionMasterKeyIds() != null && data.getEncryptionMasterKeyIds().length > 0)
|| data.getSymmetricPassphrase() != null);
boolean enableCompression = (data.getCompressionAlgorithm() != CompressionAlgorithmTags.UNCOMPRESSED);
Log.d(Constants.TAG, "enableSignature:" + enableSignature
+ "\nenableEncryption:" + enableEncryption
+ "\nenableCompression:" + enableCompression
+ "\nenableAsciiArmorOutput:" + data.isEnableAsciiArmorOutput()
+ "\nisHiddenRecipients:" + data.isHiddenRecipients());
// add additional key id to encryption ids (mostly to do self-encryption)
if (enableEncryption && data.getAdditionalEncryptId() != Constants.key.none) {
data.setEncryptionMasterKeyIds(Arrays.copyOf(data.getEncryptionMasterKeyIds(), data.getEncryptionMasterKeyIds().length + 1));
data.getEncryptionMasterKeyIds()[data.getEncryptionMasterKeyIds().length - 1] = data.getAdditionalEncryptId();
int compressionAlgorithm = data.getCompressionAlgorithm();
if (compressionAlgorithm == OpenKeychainCompressionAlgorithmTags.USE_DEFAULT) {
compressionAlgorithm = PgpSecurityConstants.DEFAULT_COMPRESSION_ALGORITHM;
}
Log.d(Constants.TAG, data.toString());
ArmoredOutputStream armorOut = null;
OutputStream out;
if (data.isEnableAsciiArmorOutput()) {
@@ -221,20 +218,26 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
try {
long signingMasterKeyId = data.getSignatureMasterKeyId();
long signingSubKeyId = data.getSignatureSubKeyId();
CanonicalizedSecretKeyRing signingKeyRing =
mKeyRepository.getCanonicalizedSecretKeyRing(signingMasterKeyId);
signingKey = signingKeyRing.getSecretKey(data.getSignatureSubKeyId());
if (input.getAllowedKeyIds() != null) {
if (!input.getAllowedKeyIds().contains(signingMasterKeyId)) {
// this key is in our db, but NOT allowed!
log.add(LogType.MSG_PSE_ERROR_KEY_NOT_ALLOWED, indent + 1);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_KEY_DISALLOWED, log);
Long signingSubKeyId = data.getSignatureSubKeyId();
if (signingSubKeyId == null) {
try {
signingSubKeyId = mKeyRepository.getCachedPublicKeyRing(signingMasterKeyId).getSecretSignId();
} catch (PgpKeyNotFoundException e) {
log.add(LogType.MSG_PSE_ERROR_KEY_SIGN, indent);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
}
CanonicalizedSecretKeyRing signingKeyRing =
mKeyRepository.getCanonicalizedSecretKeyRing(signingMasterKeyId);
signingKey = signingKeyRing.getSecretKey(signingSubKeyId);
Collection<Long> allowedSigningKeyIds = data.getAllowedSigningKeyIds();
if (allowedSigningKeyIds != null && !allowedSigningKeyIds.contains(signingMasterKeyId)) {
// this key is in our db, but NOT allowed!
log.add(LogType.MSG_PSE_ERROR_KEY_NOT_ALLOWED, indent + 1);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_KEY_DISALLOWED, log);
}
// Make sure key is not expired or revoked
if (signingKeyRing.isExpired() || signingKeyRing.isRevoked()
@@ -300,12 +303,6 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
log.add(LogType.MSG_PSE_ERROR_UNLOCK, indent);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
// Use requested hash algo
int requestedAlgorithm = data.getSignatureHashAlgorithm();
if (requestedAlgorithm == PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT) {
data.setSignatureHashAlgorithm(PgpSecurityConstants.DEFAULT_HASH_ALGORITHM);
}
}
updateProgress(R.string.progress_preparing_streams, 2, 100);
@@ -314,12 +311,12 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
if (enableEncryption) {
// Use requested encryption algo
int algo = data.getSymmetricEncryptionAlgorithm();
if (algo == PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT) {
algo = PgpSecurityConstants.DEFAULT_SYMMETRIC_ALGORITHM;
int symmetricEncryptionAlgorithm = data.getSymmetricEncryptionAlgorithm();
if (symmetricEncryptionAlgorithm == OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT) {
symmetricEncryptionAlgorithm = PgpSecurityConstants.DEFAULT_SYMMETRIC_ALGORITHM;
}
JcePGPDataEncryptorBuilder encryptorBuilder =
new JcePGPDataEncryptorBuilder(algo)
new JcePGPDataEncryptorBuilder(symmetricEncryptionAlgorithm)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
.setWithIntegrityPacket(true);
@@ -336,36 +333,28 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
log.add(LogType.MSG_PSE_ASYMMETRIC, indent);
// Asymmetric encryption
for (long id : data.getEncryptionMasterKeyIds()) {
try {
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(id));
Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
for (Long subKeyId : encryptSubKeyIds) {
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
cPk.addMethod(key.getPubKeyEncryptionGenerator(data.isHiddenRecipients()));
log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(subKeyId));
}
if (encryptSubKeyIds.isEmpty()) {
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
// Make sure key is not expired or revoked
if (keyRing.isExpired() || keyRing.isRevoked()) {
log.add(LogType.MSG_PSE_ERROR_REVOKED_OR_EXPIRED, indent);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
} catch (KeyWritableRepository.NotFoundException e) {
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
for (long encryptMasterKeyId : data.getEncryptionMasterKeyIds()) {
boolean success = processEncryptionMasterKeyId(indent, log, data, cPk, encryptMasterKeyId);
if (!success) {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
}
long additionalEncryptId = data.getAdditionalEncryptId();
if (additionalEncryptId != Constants.key.none) {
boolean success = processEncryptionMasterKeyId(indent, log, data, cPk, additionalEncryptId);
if (!success) {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
}
}
}
int signatureHashAlgorithm = data.getSignatureHashAlgorithm();
if (signatureHashAlgorithm == OpenKeychainHashAlgorithmTags.USE_DEFAULT) {
signatureHashAlgorithm = PgpSecurityConstants.DEFAULT_HASH_ALGORITHM;
}
/* Initialize signature generator object for later usage */
PGPSignatureGenerator signatureGenerator = null;
if (enableSignature) {
@@ -374,7 +363,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
try {
boolean cleartext = data.isCleartextSignature() && data.isEnableAsciiArmorOutput() && !enableEncryption;
signatureGenerator = signingKey.getDataSignatureGenerator(
data.getSignatureHashAlgorithm(), cleartext,
signatureHashAlgorithm, cleartext,
cryptoInput.getCryptoData(), cryptoInput.getSignatureTime());
} catch (PgpGeneralException e) {
log.add(LogType.MSG_PSE_ERROR_NFC, indent);
@@ -409,15 +398,10 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
encryptionOut = cPk.open(out, new byte[1 << 16]);
if (enableCompression) {
if (compressionAlgorithm != CompressionAlgorithmTags.UNCOMPRESSED) {
log.add(LogType.MSG_PSE_COMPRESSING, indent);
// Use preferred compression algo
int algo = data.getCompressionAlgorithm();
if (algo == PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT) {
algo = PgpSecurityConstants.DEFAULT_COMPRESSION_ALGORITHM;
}
compressGen = new PGPCompressedDataGenerator(algo);
compressGen = new PGPCompressedDataGenerator(compressionAlgorithm);
bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
} else {
bcpgOut = new BCPGOutputStream(encryptionOut);
@@ -466,7 +450,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
log.add(LogType.MSG_PSE_SIGNING_CLEARTEXT, indent);
// write -----BEGIN PGP SIGNED MESSAGE-----
armorOut.beginClearText(data.getSignatureHashAlgorithm());
armorOut.beginClearText(signatureHashAlgorithm);
InputStream in = new BufferedInputStream(inputData.getInputStream());
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
@@ -539,14 +523,10 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
InputStream in = new BufferedInputStream(inputData.getInputStream());
if (enableCompression) {
// Use preferred compression algo
int algo = data.getCompressionAlgorithm();
if (algo == PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT) {
algo = PgpSecurityConstants.DEFAULT_COMPRESSION_ALGORITHM;
}
if (compressionAlgorithm != CompressionAlgorithmTags.UNCOMPRESSED) {
log.add(LogType.MSG_PSE_COMPRESSING, indent);
compressGen = new PGPCompressedDataGenerator(algo);
compressGen = new PGPCompressedDataGenerator(compressionAlgorithm);
bcpgOut = new BCPGOutputStream(compressGen.open(out));
} else {
bcpgOut = new BCPGOutputStream(out);
@@ -597,16 +577,15 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
}
opTime = System.currentTimeMillis() - startTime;
Log.d(Constants.TAG, "sign/encrypt time taken: " + String.format("%.2f",
opTime / 1000.0) + "s");
Log.d(Constants.TAG, "sign/encrypt time taken: " + String.format("%.2f", opTime / 1000.0) + "s");
// closing outputs
// NOTE: closing needs to be done in the correct order!
if (encryptionOut != null) {
if (compressGen != null) {
compressGen.close();
}
if (compressGen != null) {
compressGen.close();
}
if (encryptionOut != null) {
encryptionOut.close();
}
// Note: Closing ArmoredOutputStream does not close the underlying stream
@@ -653,7 +632,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
}
result.setDetachedSignature(detachedByteOut.toByteArray());
try {
String digestName = PGPUtil.getDigestName(data.getSignatureHashAlgorithm());
String digestName = PGPUtil.getDigestName(signatureHashAlgorithm);
// construct micalg parameter according to https://tools.ietf.org/html/rfc3156#section-5
result.setMicAlgDigestName("pgp-" + digestName.toLowerCase());
} catch (PGPException e) {
@@ -663,6 +642,36 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
return result;
}
private boolean processEncryptionMasterKeyId(int indent, OperationLog log, PgpSignEncryptData data,
PGPEncryptedDataGenerator cPk, long encryptMasterKeyId) {
try {
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(encryptMasterKeyId));
Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
for (Long subKeyId : encryptSubKeyIds) {
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
cPk.addMethod(key.getPubKeyEncryptionGenerator(data.isHiddenRecipients()));
log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(subKeyId));
}
if (encryptSubKeyIds.isEmpty()) {
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(encryptMasterKeyId));
return false;
}
// Make sure key is not expired or revoked
if (keyRing.isExpired() || keyRing.isRevoked()) {
log.add(LogType.MSG_PSE_ERROR_REVOKED_OR_EXPIRED, indent);
return false;
}
} catch (KeyWritableRepository.NotFoundException e) {
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(encryptMasterKeyId));
return false;
}
return true;
}
/**
* Remove whitespaces on line endings
*/

View File

@@ -19,14 +19,17 @@
package org.sufficientlysecure.keychain.pgp;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.google.auto.value.AutoValue;
/**
* This parcel stores the input of one or more PgpSignEncrypt operations.
* All operations will use the same general parameters, differing only in
@@ -39,83 +42,65 @@ import java.util.List;
* - Once the output uris are empty, there must be exactly one input (uri xor bytes)
* left, which will be returned in a byte array as part of the result parcel.
*/
public class SignEncryptParcel implements Parcelable {
@AutoValue
public abstract class SignEncryptParcel implements Parcelable {
public abstract PgpSignEncryptData getSignEncryptData();
public abstract List<Uri> getInputUris();
public abstract List<Uri> getOutputUris();
@SuppressWarnings("mutable")
@Nullable
public abstract byte[] getBytes();
private PgpSignEncryptData data;
public ArrayList<Uri> mInputUris = new ArrayList<>();
public ArrayList<Uri> mOutputUris = new ArrayList<>();
public byte[] mBytes;
public SignEncryptParcel(PgpSignEncryptData data) {
this.data = data;
}
public SignEncryptParcel(Parcel src) {
mInputUris = src.createTypedArrayList(Uri.CREATOR);
mOutputUris = src.createTypedArrayList(Uri.CREATOR);
mBytes = src.createByteArray();
data = src.readParcelable(getClass().getClassLoader());
}
public boolean isIncomplete() {
return mInputUris.size() > mOutputUris.size();
List<Uri> inputUris = getInputUris();
List<Uri> outputUris = getOutputUris();
if (inputUris == null || outputUris == null) {
throw new IllegalStateException("Invalid operation for bytes-backed SignEncryptParcel!");
}
return inputUris.size() > outputUris.size();
}
public byte[] getBytes() {
return mBytes;
public static SignEncryptParcel createSignEncryptParcel(PgpSignEncryptData signEncryptData, byte[] bytes) {
// noinspection unchecked, it's ok for the empty list
return new AutoValue_SignEncryptParcel(signEncryptData, Collections.EMPTY_LIST, Collections.EMPTY_LIST, bytes);
}
public void setBytes(byte[] bytes) {
mBytes = bytes;
public static Builder builder(SignEncryptParcel signEncryptParcel) {
return new Builder(signEncryptParcel.getSignEncryptData())
.addInputUris(signEncryptParcel.getInputUris())
.addOutputUris(signEncryptParcel.getOutputUris());
}
public List<Uri> getInputUris() {
return Collections.unmodifiableList(mInputUris);
public static Builder builder(PgpSignEncryptData signEncryptData) {
return new Builder(signEncryptData);
}
public void addInputUris(Collection<Uri> inputUris) {
mInputUris.addAll(inputUris);
}
public List<Uri> getOutputUris() {
return Collections.unmodifiableList(mOutputUris);
}
public static class Builder {
private final PgpSignEncryptData signEncryptData;
private ArrayList<Uri> inputUris = new ArrayList<>();
private ArrayList<Uri> outputUris = new ArrayList<>();
public void addOutputUris(ArrayList<Uri> outputUris) {
mOutputUris.addAll(outputUris);
}
public void setData(PgpSignEncryptData data) {
this.data = data;
}
public PgpSignEncryptData getData() {
return data;
}
@Override
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(mInputUris);
dest.writeTypedList(mOutputUris);
dest.writeByteArray(mBytes);
dest.writeParcelable(data, 0);
}
public static final Creator<SignEncryptParcel> CREATOR = new Creator<SignEncryptParcel>() {
public SignEncryptParcel createFromParcel(final Parcel source) {
return new SignEncryptParcel(source);
private Builder(PgpSignEncryptData signEncryptData) {
this.signEncryptData = signEncryptData;
}
public SignEncryptParcel[] newArray(final int size) {
return new SignEncryptParcel[size];
public SignEncryptParcel build() {
return new AutoValue_SignEncryptParcel(signEncryptData,
Collections.unmodifiableList(inputUris),
Collections.unmodifiableList(outputUris),
null);
}
};
public Builder addOutputUris(Collection<Uri> outputUris) {
this.outputUris.addAll(outputUris);
return this;
}
public Builder addInputUris(Collection<Uri> inputUris) {
this.inputUris.addAll(inputUris);
return this;
}
}
}

View File

@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.pgp;
import com.google.auto.value.AutoValue;
import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.Packet;