@@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user