Merge branch 'master' of github.com:open-keychain/open-keychain
This commit is contained in:
@@ -242,7 +242,7 @@ android {
|
|||||||
|
|
||||||
// apply plugin: 'spoon'
|
// apply plugin: 'spoon'
|
||||||
|
|
||||||
task jacocoTestReport(type:JacocoReport) {
|
task jacocoTestReport(type:JacocoReport, dependsOn: "testDebugUnitTest") {
|
||||||
group = "Reporting"
|
group = "Reporting"
|
||||||
description = "Generate Jacoco coverage reports"
|
description = "Generate Jacoco coverage reports"
|
||||||
|
|
||||||
@@ -252,7 +252,9 @@ task jacocoTestReport(type:JacocoReport) {
|
|||||||
'**/R$*.class',
|
'**/R$*.class',
|
||||||
'**/*$ViewInjector*.*',
|
'**/*$ViewInjector*.*',
|
||||||
'**/BuildConfig.*',
|
'**/BuildConfig.*',
|
||||||
'**/Manifest*.*']
|
'**/Manifest*.*',
|
||||||
|
'**/*Activity*.*',
|
||||||
|
'**/*Fragment*.*']
|
||||||
)
|
)
|
||||||
|
|
||||||
sourceDirectories = files("${buildDir.parent}/src/main/java")
|
sourceDirectories = files("${buildDir.parent}/src/main/java")
|
||||||
@@ -260,10 +262,7 @@ task jacocoTestReport(type:JacocoReport) {
|
|||||||
"${buildDir}/generated/source/buildConfig/debug",
|
"${buildDir}/generated/source/buildConfig/debug",
|
||||||
"${buildDir}/generated/source/r/debug"
|
"${buildDir}/generated/source/r/debug"
|
||||||
])
|
])
|
||||||
executionData = files([
|
executionData = fileTree(dir: "${buildDir}/jacoco", include: "**/*.exec")
|
||||||
"${buildDir}/jacoco/testDebug.exec",
|
|
||||||
"${buildDir}/outputs/code-coverage/connected/coverage.ec"
|
|
||||||
])
|
|
||||||
|
|
||||||
reports {
|
reports {
|
||||||
xml.enabled = true
|
xml.enabled = true
|
||||||
|
|||||||
@@ -151,7 +151,6 @@ public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificat
|
|||||||
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(mContext, mProviderHelper, mProgressable);
|
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(mContext, mProviderHelper, mProgressable);
|
||||||
|
|
||||||
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
|
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
|
||||||
.setSignedLiteralData(true)
|
|
||||||
.setRequiredSignerFingerprint(requiredFingerprint);
|
.setRequiredSignerFingerprint(requiredFingerprint);
|
||||||
|
|
||||||
DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
|
DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
|
||||||
|
|||||||
@@ -661,6 +661,7 @@ public abstract class OperationResult implements Parcelable {
|
|||||||
MSG_DC_ERROR_INPUT (LogLevel.ERROR, R.string.msg_dc_error_input),
|
MSG_DC_ERROR_INPUT (LogLevel.ERROR, R.string.msg_dc_error_input),
|
||||||
MSG_DC_ERROR_NO_DATA (LogLevel.ERROR, R.string.msg_dc_error_no_data),
|
MSG_DC_ERROR_NO_DATA (LogLevel.ERROR, R.string.msg_dc_error_no_data),
|
||||||
MSG_DC_ERROR_NO_KEY (LogLevel.ERROR, R.string.msg_dc_error_no_key),
|
MSG_DC_ERROR_NO_KEY (LogLevel.ERROR, R.string.msg_dc_error_no_key),
|
||||||
|
MSG_DC_ERROR_NO_SIGNATURE (LogLevel.ERROR, R.string.msg_dc_error_no_signature),
|
||||||
MSG_DC_ERROR_PGP_EXCEPTION (LogLevel.ERROR, R.string.msg_dc_error_pgp_exception),
|
MSG_DC_ERROR_PGP_EXCEPTION (LogLevel.ERROR, R.string.msg_dc_error_pgp_exception),
|
||||||
MSG_DC_INTEGRITY_CHECK_OK (LogLevel.INFO, R.string.msg_dc_integrity_check_ok),
|
MSG_DC_INTEGRITY_CHECK_OK (LogLevel.INFO, R.string.msg_dc_integrity_check_ok),
|
||||||
MSG_DC_OK_META_ONLY (LogLevel.OK, R.string.msg_dc_ok_meta_only),
|
MSG_DC_OK_META_ONLY (LogLevel.OK, R.string.msg_dc_ok_meta_only),
|
||||||
@@ -687,6 +688,7 @@ public abstract class OperationResult implements Parcelable {
|
|||||||
MSG_VL_ERROR_MISSING_SIGLIST (LogLevel.ERROR, R.string.msg_vl_error_no_siglist),
|
MSG_VL_ERROR_MISSING_SIGLIST (LogLevel.ERROR, R.string.msg_vl_error_no_siglist),
|
||||||
MSG_VL_ERROR_MISSING_LITERAL (LogLevel.ERROR, R.string.msg_vl_error_missing_literal),
|
MSG_VL_ERROR_MISSING_LITERAL (LogLevel.ERROR, R.string.msg_vl_error_missing_literal),
|
||||||
MSG_VL_ERROR_MISSING_KEY (LogLevel.ERROR, R.string.msg_vl_error_wrong_key),
|
MSG_VL_ERROR_MISSING_KEY (LogLevel.ERROR, R.string.msg_vl_error_wrong_key),
|
||||||
|
MSG_VL_ERROR_NO_SIGNATURE (LogLevel.ERROR, R.string.msg_vl_error_no_signature),
|
||||||
MSG_VL_CLEAR_SIGNATURE_CHECK (LogLevel.DEBUG, R.string.msg_vl_clear_signature_check),
|
MSG_VL_CLEAR_SIGNATURE_CHECK (LogLevel.DEBUG, R.string.msg_vl_clear_signature_check),
|
||||||
MSG_VL_ERROR_INTEGRITY_CHECK (LogLevel.ERROR, R.string.msg_vl_error_integrity_check),
|
MSG_VL_ERROR_INTEGRITY_CHECK (LogLevel.ERROR, R.string.msg_vl_error_integrity_check),
|
||||||
MSG_VL_OK (LogLevel.OK, R.string.msg_vl_ok),
|
MSG_VL_OK (LogLevel.OK, R.string.msg_vl_ok),
|
||||||
@@ -703,7 +705,6 @@ public abstract class OperationResult implements Parcelable {
|
|||||||
|
|
||||||
// pgpsignencrypt
|
// pgpsignencrypt
|
||||||
MSG_PSE_ASYMMETRIC (LogLevel.INFO, R.string.msg_pse_asymmetric),
|
MSG_PSE_ASYMMETRIC (LogLevel.INFO, R.string.msg_pse_asymmetric),
|
||||||
MSG_PSE_CLEARSIGN_ONLY (LogLevel.DEBUG, R.string.msg_pse_clearsign_only),
|
|
||||||
MSG_PSE_COMPRESSING (LogLevel.DEBUG, R.string.msg_pse_compressing),
|
MSG_PSE_COMPRESSING (LogLevel.DEBUG, R.string.msg_pse_compressing),
|
||||||
MSG_PSE_ENCRYPTING (LogLevel.DEBUG, R.string.msg_pse_encrypting),
|
MSG_PSE_ENCRYPTING (LogLevel.DEBUG, R.string.msg_pse_encrypting),
|
||||||
MSG_PSE_ERROR_BAD_PASSPHRASE (LogLevel.ERROR, R.string.msg_pse_error_bad_passphrase),
|
MSG_PSE_ERROR_BAD_PASSPHRASE (LogLevel.ERROR, R.string.msg_pse_error_bad_passphrase),
|
||||||
|
|||||||
@@ -44,13 +44,17 @@ import java.util.Iterator;
|
|||||||
public class CanonicalizedPublicKey extends UncachedPublicKey {
|
public class CanonicalizedPublicKey extends UncachedPublicKey {
|
||||||
|
|
||||||
// this is the parent key ring
|
// this is the parent key ring
|
||||||
final KeyRing mRing;
|
final CanonicalizedKeyRing mRing;
|
||||||
|
|
||||||
CanonicalizedPublicKey(KeyRing ring, PGPPublicKey key) {
|
CanonicalizedPublicKey(CanonicalizedKeyRing ring, PGPPublicKey key) {
|
||||||
super(key);
|
super(key);
|
||||||
mRing = ring;
|
mRing = ring;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CanonicalizedKeyRing getKeyRing() {
|
||||||
|
return mRing;
|
||||||
|
}
|
||||||
|
|
||||||
public IterableIterator<String> getUserIds() {
|
public IterableIterator<String> getUserIds() {
|
||||||
return new IterableIterator<String>(mPublicKey.getUserIDs());
|
return new IterableIterator<String>(mPublicKey.getUserIDs());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,11 +91,12 @@ public class OpenPgpSignatureResultBuilder {
|
|||||||
return mInsecure;
|
return mInsecure;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initValid(CanonicalizedPublicKeyRing signingRing,
|
public void initValid(CanonicalizedPublicKey signingKey) {
|
||||||
CanonicalizedPublicKey signingKey) {
|
|
||||||
setSignatureAvailable(true);
|
setSignatureAvailable(true);
|
||||||
setKnownKey(true);
|
setKnownKey(true);
|
||||||
|
|
||||||
|
CanonicalizedKeyRing signingRing = signingKey.getKeyRing();
|
||||||
|
|
||||||
// from RING
|
// from RING
|
||||||
setKeyId(signingRing.getMasterKeyId());
|
setKeyId(signingRing.getMasterKeyId());
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -36,7 +36,6 @@ public class PgpDecryptVerifyInputParcel implements Parcelable {
|
|||||||
private boolean mDecryptMetadataOnly;
|
private boolean mDecryptMetadataOnly;
|
||||||
private byte[] mDetachedSignature;
|
private byte[] mDetachedSignature;
|
||||||
private String mRequiredSignerFingerprint;
|
private String mRequiredSignerFingerprint;
|
||||||
private boolean mSignedLiteralData;
|
|
||||||
|
|
||||||
public PgpDecryptVerifyInputParcel() {
|
public PgpDecryptVerifyInputParcel() {
|
||||||
}
|
}
|
||||||
@@ -61,7 +60,6 @@ public class PgpDecryptVerifyInputParcel implements Parcelable {
|
|||||||
mDecryptMetadataOnly = source.readInt() != 0;
|
mDecryptMetadataOnly = source.readInt() != 0;
|
||||||
mDetachedSignature = source.createByteArray();
|
mDetachedSignature = source.createByteArray();
|
||||||
mRequiredSignerFingerprint = source.readString();
|
mRequiredSignerFingerprint = source.readString();
|
||||||
mSignedLiteralData = source.readInt() != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,7 +78,6 @@ public class PgpDecryptVerifyInputParcel implements Parcelable {
|
|||||||
dest.writeInt(mDecryptMetadataOnly ? 1 : 0);
|
dest.writeInt(mDecryptMetadataOnly ? 1 : 0);
|
||||||
dest.writeByteArray(mDetachedSignature);
|
dest.writeByteArray(mDetachedSignature);
|
||||||
dest.writeString(mRequiredSignerFingerprint);
|
dest.writeString(mRequiredSignerFingerprint);
|
||||||
dest.writeInt(mSignedLiteralData ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] getInputBytes() {
|
byte[] getInputBytes() {
|
||||||
@@ -150,15 +147,6 @@ public class PgpDecryptVerifyInputParcel implements Parcelable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isSignedLiteralData() {
|
|
||||||
return mSignedLiteralData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PgpDecryptVerifyInputParcel setSignedLiteralData(boolean signedLiteralData) {
|
|
||||||
mSignedLiteralData = signedLiteralData;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Creator<PgpDecryptVerifyInputParcel> CREATOR = new Creator<PgpDecryptVerifyInputParcel>() {
|
public static final Creator<PgpDecryptVerifyInputParcel> CREATOR = new Creator<PgpDecryptVerifyInputParcel>() {
|
||||||
public PgpDecryptVerifyInputParcel createFromParcel(final Parcel source) {
|
public PgpDecryptVerifyInputParcel createFromParcel(final Parcel source) {
|
||||||
return new PgpDecryptVerifyInputParcel(source);
|
return new PgpDecryptVerifyInputParcel(source);
|
||||||
|
|||||||
@@ -18,13 +18,23 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
||||||
import org.openintents.openpgp.OpenPgpMetadata;
|
import org.openintents.openpgp.OpenPgpMetadata;
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
import org.spongycastle.bcpg.ArmoredInputStream;
|
import org.spongycastle.bcpg.ArmoredInputStream;
|
||||||
import org.spongycastle.openpgp.PGPCompressedData;
|
import org.spongycastle.openpgp.PGPCompressedData;
|
||||||
import org.spongycastle.openpgp.PGPDataValidationException;
|
import org.spongycastle.openpgp.PGPDataValidationException;
|
||||||
@@ -33,18 +43,14 @@ import org.spongycastle.openpgp.PGPEncryptedDataList;
|
|||||||
import org.spongycastle.openpgp.PGPException;
|
import org.spongycastle.openpgp.PGPException;
|
||||||
import org.spongycastle.openpgp.PGPKeyValidationException;
|
import org.spongycastle.openpgp.PGPKeyValidationException;
|
||||||
import org.spongycastle.openpgp.PGPLiteralData;
|
import org.spongycastle.openpgp.PGPLiteralData;
|
||||||
import org.spongycastle.openpgp.PGPOnePassSignature;
|
|
||||||
import org.spongycastle.openpgp.PGPOnePassSignatureList;
|
|
||||||
import org.spongycastle.openpgp.PGPPBEEncryptedData;
|
import org.spongycastle.openpgp.PGPPBEEncryptedData;
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
|
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||||
import org.spongycastle.openpgp.PGPSignature;
|
|
||||||
import org.spongycastle.openpgp.PGPSignatureList;
|
import org.spongycastle.openpgp.PGPSignatureList;
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
import org.spongycastle.openpgp.PGPUtil;
|
||||||
import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
|
import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
|
||||||
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
|
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
|
||||||
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
|
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
|
import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
|
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
|
||||||
import org.spongycastle.util.encoders.DecoderException;
|
import org.spongycastle.util.encoders.DecoderException;
|
||||||
@@ -68,17 +74,6 @@ import org.sufficientlysecure.keychain.util.Log;
|
|||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInputParcel> {
|
public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInputParcel> {
|
||||||
|
|
||||||
public PgpDecryptVerifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
|
public PgpDecryptVerifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
|
||||||
@@ -153,9 +148,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
// it is ascii armored
|
// it is ascii armored
|
||||||
Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine());
|
Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine());
|
||||||
|
|
||||||
if (input.isSignedLiteralData()) {
|
if (aIn.isClearText()) {
|
||||||
return verifySignedLiteralData(input, aIn, outputStream, 0);
|
|
||||||
} else if (aIn.isClearText()) {
|
|
||||||
// a cleartext signature, verify it with the other method
|
// a cleartext signature, verify it with the other method
|
||||||
return verifyCleartextSignature(aIn, outputStream, 0);
|
return verifyCleartextSignature(aIn, outputStream, 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -186,133 +179,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Verify signed plaintext data (PGP/INLINE). */
|
|
||||||
@NonNull
|
|
||||||
private DecryptVerifyResult verifySignedLiteralData(
|
|
||||||
PgpDecryptVerifyInputParcel input, InputStream in, OutputStream out, int indent)
|
|
||||||
throws IOException, PGPException {
|
|
||||||
OperationLog log = new OperationLog();
|
|
||||||
log.add(LogType.MSG_VL, indent);
|
|
||||||
|
|
||||||
// thinking that the proof-fetching operation is going to take most of the time
|
|
||||||
updateProgress(R.string.progress_reading_data, 75, 100);
|
|
||||||
|
|
||||||
JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
|
|
||||||
Object o = pgpF.nextObject();
|
|
||||||
if (o instanceof PGPCompressedData) {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1);
|
|
||||||
|
|
||||||
pgpF = new JcaPGPObjectFactory(((PGPCompressedData) o).getDataStream());
|
|
||||||
o = pgpF.nextObject();
|
|
||||||
updateProgress(R.string.progress_decompressing_data, 80, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// all we want to see is a OnePassSignatureList followed by LiteralData
|
|
||||||
if (!(o instanceof PGPOnePassSignatureList)) {
|
|
||||||
log.add(LogType.MSG_VL_ERROR_MISSING_SIGLIST, indent);
|
|
||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
|
||||||
}
|
|
||||||
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) o;
|
|
||||||
|
|
||||||
// go through all signatures (should be just one), make sure we have
|
|
||||||
// the key and it matches the one we’re looking for
|
|
||||||
CanonicalizedPublicKeyRing signingRing = null;
|
|
||||||
CanonicalizedPublicKey signingKey = null;
|
|
||||||
int signatureIndex = -1;
|
|
||||||
for (int i = 0; i < sigList.size(); ++i) {
|
|
||||||
try {
|
|
||||||
long sigKeyId = sigList.get(i).getKeyID();
|
|
||||||
signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
|
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
|
|
||||||
);
|
|
||||||
signingKey = signingRing.getPublicKey(sigKeyId);
|
|
||||||
signatureIndex = i;
|
|
||||||
} catch (ProviderHelper.NotFoundException e) {
|
|
||||||
Log.d(Constants.TAG, "key not found, trying next signature...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// there has to be a key, and it has to be the right one
|
|
||||||
if (signingKey == null) {
|
|
||||||
log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent);
|
|
||||||
Log.d(Constants.TAG, "Failed to find key in signed-literal message");
|
|
||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(signingRing.getFingerprint());
|
|
||||||
if (!(input.getRequiredSignerFingerprint().equals(fingerprint))) {
|
|
||||||
log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent);
|
|
||||||
Log.d(Constants.TAG, "Fingerprint mismatch; wanted " + input.getRequiredSignerFingerprint() +
|
|
||||||
" got " + fingerprint + "!");
|
|
||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
|
|
||||||
|
|
||||||
PGPOnePassSignature signature = sigList.get(signatureIndex);
|
|
||||||
signatureResultBuilder.initValid(signingRing, signingKey);
|
|
||||||
|
|
||||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
|
||||||
new JcaPGPContentVerifierBuilderProvider()
|
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
|
||||||
signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
|
|
||||||
|
|
||||||
o = pgpF.nextObject();
|
|
||||||
|
|
||||||
if (!(o instanceof PGPLiteralData)) {
|
|
||||||
log.add(LogType.MSG_VL_ERROR_MISSING_LITERAL, indent);
|
|
||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPLiteralData literalData = (PGPLiteralData) o;
|
|
||||||
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1);
|
|
||||||
updateProgress(R.string.progress_decrypting, 85, 100);
|
|
||||||
|
|
||||||
InputStream dataIn = literalData.getInputStream();
|
|
||||||
|
|
||||||
int length;
|
|
||||||
byte[] buffer = new byte[1 << 16];
|
|
||||||
while ((length = dataIn.read(buffer)) > 0) {
|
|
||||||
out.write(buffer, 0, length);
|
|
||||||
signature.update(buffer, 0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProgress(R.string.progress_verifying_signature, 95, 100);
|
|
||||||
log.add(LogType.MSG_VL_CLEAR_SIGNATURE_CHECK, indent + 1);
|
|
||||||
|
|
||||||
PGPSignatureList signatureList = (PGPSignatureList) pgpF.nextObject();
|
|
||||||
PGPSignature messageSignature = signatureList.get(signatureIndex);
|
|
||||||
|
|
||||||
// Verify signature and check binding signatures
|
|
||||||
boolean validSignature = signature.verify(messageSignature);
|
|
||||||
if (validSignature) {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
|
|
||||||
} else {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
|
||||||
}
|
|
||||||
signatureResultBuilder.setValidSignature(validSignature);
|
|
||||||
|
|
||||||
OpenPgpSignatureResult signatureResult = signatureResultBuilder.build();
|
|
||||||
|
|
||||||
if (signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_CONFIRMED
|
|
||||||
&& signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED) {
|
|
||||||
log.add(LogType.MSG_VL_ERROR_INTEGRITY_CHECK, indent);
|
|
||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
|
||||||
|
|
||||||
log.add(LogType.MSG_VL_OK, indent);
|
|
||||||
|
|
||||||
// Return a positive result, with metadata and verification info
|
|
||||||
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
|
||||||
result.setSignatureResult(signatureResult);
|
|
||||||
result.setDecryptionResult(
|
|
||||||
new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class EncryptStreamResult {
|
private static class EncryptStreamResult {
|
||||||
|
|
||||||
// this is non-null iff an error occured, return directly
|
// this is non-null iff an error occured, return directly
|
||||||
@@ -368,7 +234,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
|
|
||||||
OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
|
OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
|
||||||
|
|
||||||
JcaPGPObjectFactory plainFact;
|
JcaPGPObjectFactory plainFact;
|
||||||
@@ -415,10 +280,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
|
|
||||||
log.add(LogType.MSG_DC_PREP_STREAMS, indent);
|
log.add(LogType.MSG_DC_PREP_STREAMS, indent);
|
||||||
|
|
||||||
int signatureIndex = -1;
|
|
||||||
CanonicalizedPublicKeyRing signingRing = null;
|
|
||||||
CanonicalizedPublicKey signingKey = null;
|
|
||||||
|
|
||||||
log.add(LogType.MSG_DC_CLEAR, indent);
|
log.add(LogType.MSG_DC_CLEAR, indent);
|
||||||
indent += 1;
|
indent += 1;
|
||||||
|
|
||||||
@@ -435,58 +296,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
plainFact = fact;
|
plainFact = fact;
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve leading signature data
|
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mProviderHelper);
|
||||||
PGPOnePassSignature signature = null;
|
if (signatureChecker.initializeOnePassSignature(dataChunk, log, indent +1)) {
|
||||||
if (dataChunk instanceof PGPOnePassSignatureList) {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent + 1);
|
|
||||||
currentProgress += 2;
|
|
||||||
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
|
|
||||||
|
|
||||||
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
|
|
||||||
|
|
||||||
// NOTE: following code is similar to processSignature, but for PGPOnePassSignature
|
|
||||||
|
|
||||||
// go through all signatures
|
|
||||||
// and find out for which signature we have a key in our database
|
|
||||||
for (int i = 0; i < sigList.size(); ++i) {
|
|
||||||
try {
|
|
||||||
long sigKeyId = sigList.get(i).getKeyID();
|
|
||||||
signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
|
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
|
|
||||||
);
|
|
||||||
signingKey = signingRing.getPublicKey(sigKeyId);
|
|
||||||
signatureIndex = i;
|
|
||||||
} catch (ProviderHelper.NotFoundException e) {
|
|
||||||
Log.d(Constants.TAG, "key not found, trying next signature...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signingKey != null) {
|
|
||||||
// key found in our database!
|
|
||||||
signature = sigList.get(signatureIndex);
|
|
||||||
|
|
||||||
signatureResultBuilder.initValid(signingRing, signingKey);
|
|
||||||
|
|
||||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
|
||||||
new JcaPGPContentVerifierBuilderProvider()
|
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
|
||||||
signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
|
|
||||||
} else {
|
|
||||||
// no key in our database -> return "unknown pub key" status including the first key id
|
|
||||||
if (!sigList.isEmpty()) {
|
|
||||||
signatureResultBuilder.setSignatureAvailable(true);
|
|
||||||
signatureResultBuilder.setKnownKey(false);
|
|
||||||
signatureResultBuilder.setKeyId(sigList.get(0).getKeyID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for insecure signing key
|
|
||||||
// TODO: checks on signingRing ?
|
|
||||||
if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) {
|
|
||||||
log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
|
|
||||||
signatureResultBuilder.setInsecure(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
dataChunk = plainFact.nextObject();
|
dataChunk = plainFact.nextObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,16 +378,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int endProgress;
|
|
||||||
if (signature != null) {
|
|
||||||
endProgress = 90;
|
|
||||||
} else if (esResult != null && esResult.encryptedData.isIntegrityProtected()) {
|
|
||||||
endProgress = 95;
|
|
||||||
} else {
|
|
||||||
endProgress = 100;
|
|
||||||
}
|
|
||||||
ProgressScaler progressScaler =
|
ProgressScaler progressScaler =
|
||||||
new ProgressScaler(mProgressable, currentProgress, endProgress, 100);
|
new ProgressScaler(mProgressable, currentProgress, 95, 100);
|
||||||
|
|
||||||
InputStream dataIn = literalData.getInputStream();
|
InputStream dataIn = literalData.getInputStream();
|
||||||
|
|
||||||
@@ -591,11 +394,10 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update signature buffer if signature is also present
|
// update signature buffer if signature is also present
|
||||||
if (signature != null) {
|
signatureChecker.updateSignatureData(buffer, 0, length);
|
||||||
signature.update(buffer, 0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
alreadyWritten += length;
|
alreadyWritten += length;
|
||||||
|
// noinspection ConstantConditions, TODO progress
|
||||||
if (wholeSize > 0) {
|
if (wholeSize > 0) {
|
||||||
long progress = 100 * alreadyWritten / wholeSize;
|
long progress = 100 * alreadyWritten / wholeSize;
|
||||||
// stop at 100% for wrong file sizes...
|
// stop at 100% for wrong file sizes...
|
||||||
@@ -604,34 +406,20 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
}
|
}
|
||||||
progressScaler.setProgress((int) progress, 100);
|
progressScaler.setProgress((int) progress, 100);
|
||||||
}
|
}
|
||||||
// TODO: slow annealing to fake a progress?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata = new OpenPgpMetadata(
|
metadata = new OpenPgpMetadata(
|
||||||
originalFilename, mimeType, literalData.getModificationTime().getTime(), alreadyWritten, charset);
|
originalFilename, mimeType, literalData.getModificationTime().getTime(), alreadyWritten, charset);
|
||||||
|
|
||||||
if (signature != null) {
|
if (signatureChecker.isInitialized()) {
|
||||||
updateProgress(R.string.progress_verifying_signature, 90, 100);
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
|
|
||||||
|
|
||||||
PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
|
Object o = plainFact.nextObject();
|
||||||
PGPSignature messageSignature = signatureList.get(signatureIndex);
|
boolean signatureCheckOk = signatureChecker.verifySignatureOnePass(o, log, indent + 1);
|
||||||
|
|
||||||
// Verify signature
|
if (!signatureCheckOk) {
|
||||||
boolean validSignature = signature.verify(messageSignature);
|
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||||
if (validSignature) {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
|
|
||||||
} else {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for insecure hash algorithms
|
|
||||||
if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
|
|
||||||
log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
|
|
||||||
signatureResultBuilder.setInsecure(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
signatureResultBuilder.setValidSignature(validSignature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
indent -= 1;
|
indent -= 1;
|
||||||
@@ -646,7 +434,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent);
|
log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent);
|
||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||||
}
|
}
|
||||||
} else if (signature == null) {
|
} else if ( ! signatureChecker.isInitialized() ) {
|
||||||
// If no signature is present, we *require* an MDC!
|
// If no signature is present, we *require* an MDC!
|
||||||
// Handle missing integrity protection like failed integrity protection!
|
// Handle missing integrity protection like failed integrity protection!
|
||||||
// The MDC packet can be stripped by an attacker!
|
// The MDC packet can be stripped by an attacker!
|
||||||
@@ -663,7 +451,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
||||||
|
|
||||||
result.setCachedCryptoInputParcel(cryptoInput);
|
result.setCachedCryptoInputParcel(cryptoInput);
|
||||||
result.setSignatureResult(signatureResultBuilder.build());
|
result.setSignatureResult(signatureChecker.getSignatureResult());
|
||||||
result.setDecryptionResult(decryptionResultBuilder.build());
|
result.setDecryptionResult(decryptionResultBuilder.build());
|
||||||
result.setDecryptionMetadata(metadata);
|
result.setDecryptionMetadata(metadata);
|
||||||
|
|
||||||
@@ -674,13 +462,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
private EncryptStreamResult handleEncryptedPacket(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
|
private EncryptStreamResult handleEncryptedPacket(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
|
||||||
PGPEncryptedDataList enc, OperationLog log, int indent, int currentProgress) throws PGPException {
|
PGPEncryptedDataList enc, OperationLog log, int indent, int currentProgress) throws PGPException {
|
||||||
|
|
||||||
// TODO is this necessary?
|
|
||||||
/*
|
|
||||||
else if (obj instanceof PGPEncryptedDataList) {
|
|
||||||
enc = (PGPEncryptedDataList) pgpF.nextObject();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
EncryptStreamResult result = new EncryptStreamResult();
|
EncryptStreamResult result = new EncryptStreamResult();
|
||||||
|
|
||||||
boolean asymmetricPacketFound = false;
|
boolean asymmetricPacketFound = false;
|
||||||
@@ -721,11 +502,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
|
log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (secretKeyRing == null) {
|
|
||||||
// continue with the next packet in the while loop
|
|
||||||
log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow only specific keys for decryption?
|
// allow only specific keys for decryption?
|
||||||
if (input.getAllowedKeyIds() != null) {
|
if (input.getAllowedKeyIds() != null) {
|
||||||
@@ -745,11 +521,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
|
|
||||||
// get subkey which has been used for this encryption packet
|
// get subkey which has been used for this encryption packet
|
||||||
secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId);
|
secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId);
|
||||||
if (secretEncryptionKey == null) {
|
|
||||||
// should actually never happen, so no need to be more specific.
|
|
||||||
log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* secret key exists in database and is allowed! */
|
/* secret key exists in database and is allowed! */
|
||||||
asymmetricPacketFound = true;
|
asymmetricPacketFound = true;
|
||||||
@@ -951,30 +722,31 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
|
|
||||||
OperationLog log = new OperationLog();
|
OperationLog log = new OperationLog();
|
||||||
|
|
||||||
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
|
byte[] clearText;
|
||||||
|
{ // read cleartext
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
updateProgress(R.string.progress_reading_data, 0, 100);
|
||||||
|
|
||||||
updateProgress(R.string.progress_reading_data, 0, 100);
|
ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
|
||||||
|
int lookAhead = readInputLine(lineOut, aIn);
|
||||||
|
byte[] lineSep = getLineSeparator();
|
||||||
|
|
||||||
ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
|
byte[] line = lineOut.toByteArray();
|
||||||
int lookAhead = readInputLine(lineOut, aIn);
|
|
||||||
byte[] lineSep = getLineSeparator();
|
|
||||||
|
|
||||||
byte[] line = lineOut.toByteArray();
|
|
||||||
out.write(line, 0, getLengthWithoutSeparator(line));
|
|
||||||
out.write(lineSep);
|
|
||||||
|
|
||||||
while (lookAhead != -1 && aIn.isClearText()) {
|
|
||||||
lookAhead = readInputLine(lineOut, lookAhead, aIn);
|
|
||||||
line = lineOut.toByteArray();
|
|
||||||
out.write(line, 0, getLengthWithoutSeparator(line));
|
out.write(line, 0, getLengthWithoutSeparator(line));
|
||||||
out.write(lineSep);
|
out.write(lineSep);
|
||||||
|
|
||||||
|
while (lookAhead != -1 && aIn.isClearText()) {
|
||||||
|
lookAhead = readInputLine(lineOut, lookAhead, aIn);
|
||||||
|
line = lineOut.toByteArray();
|
||||||
|
out.write(line, 0, getLengthWithoutSeparator(line));
|
||||||
|
out.write(lineSep);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.close();
|
||||||
|
clearText = out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
byte[] clearText = out.toByteArray();
|
|
||||||
if (outputStream != null) {
|
if (outputStream != null) {
|
||||||
outputStream.write(clearText);
|
outputStream.write(clearText);
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
@@ -983,51 +755,20 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
updateProgress(R.string.progress_processing_signature, 60, 100);
|
updateProgress(R.string.progress_processing_signature, 60, 100);
|
||||||
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(aIn);
|
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(aIn);
|
||||||
|
|
||||||
PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
|
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mProviderHelper);
|
||||||
if (sigList == null) {
|
|
||||||
|
Object o = pgpFact.nextObject();
|
||||||
|
if (!signatureChecker.initializeSignature(o, log, indent+1)) {
|
||||||
log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0);
|
log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0);
|
||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent);
|
if (signatureChecker.isInitialized()) {
|
||||||
|
|
||||||
if (signature != null) {
|
|
||||||
try {
|
try {
|
||||||
updateProgress(R.string.progress_verifying_signature, 90, 100);
|
updateProgress(R.string.progress_verifying_signature, 90, 100);
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
|
|
||||||
|
|
||||||
InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText));
|
signatureChecker.updateSignatureWithCleartext(clearText);
|
||||||
|
signatureChecker.verifySignature(log, indent);
|
||||||
lookAhead = readInputLine(lineOut, sigIn);
|
|
||||||
|
|
||||||
processLine(signature, lineOut.toByteArray());
|
|
||||||
|
|
||||||
if (lookAhead != -1) {
|
|
||||||
do {
|
|
||||||
lookAhead = readInputLine(lineOut, lookAhead, sigIn);
|
|
||||||
|
|
||||||
signature.update((byte) '\r');
|
|
||||||
signature.update((byte) '\n');
|
|
||||||
|
|
||||||
processLine(signature, lineOut.toByteArray());
|
|
||||||
} while (lookAhead != -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify signature and check binding signatures
|
|
||||||
boolean validSignature = signature.verify();
|
|
||||||
if (validSignature) {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
|
|
||||||
} else {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for insecure hash algorithms
|
|
||||||
if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
|
|
||||||
log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
|
|
||||||
signatureResultBuilder.setInsecure(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
signatureResultBuilder.setValidSignature(validSignature);
|
|
||||||
|
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
Log.d(Constants.TAG, "SignatureException", e);
|
Log.d(Constants.TAG, "SignatureException", e);
|
||||||
@@ -1046,7 +787,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
clearText.length);
|
clearText.length);
|
||||||
|
|
||||||
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
||||||
result.setSignatureResult(signatureResultBuilder.build());
|
result.setSignatureResult(signatureChecker.getSignatureResult());
|
||||||
result.setDecryptionResult(
|
result.setDecryptionResult(
|
||||||
new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
|
new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
|
||||||
result.setDecryptionMetadata(metadata);
|
result.setDecryptionMetadata(metadata);
|
||||||
@@ -1060,30 +801,28 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
|
|
||||||
OperationLog log = new OperationLog();
|
OperationLog log = new OperationLog();
|
||||||
|
|
||||||
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
|
|
||||||
|
|
||||||
updateProgress(R.string.progress_processing_signature, 0, 100);
|
updateProgress(R.string.progress_processing_signature, 0, 100);
|
||||||
InputStream detachedSigIn = new ByteArrayInputStream(input.getDetachedSignature());
|
InputStream detachedSigIn = new ByteArrayInputStream(input.getDetachedSignature());
|
||||||
detachedSigIn = PGPUtil.getDecoderStream(detachedSigIn);
|
detachedSigIn = PGPUtil.getDecoderStream(detachedSigIn);
|
||||||
|
|
||||||
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(detachedSigIn);
|
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(detachedSigIn);
|
||||||
|
|
||||||
PGPSignatureList sigList;
|
|
||||||
Object o = pgpFact.nextObject();
|
Object o = pgpFact.nextObject();
|
||||||
if (o instanceof PGPCompressedData) {
|
if (o instanceof PGPCompressedData) {
|
||||||
PGPCompressedData c1 = (PGPCompressedData) o;
|
PGPCompressedData c1 = (PGPCompressedData) o;
|
||||||
pgpFact = new JcaPGPObjectFactory(c1.getDataStream());
|
pgpFact = new JcaPGPObjectFactory(c1.getDataStream());
|
||||||
sigList = (PGPSignatureList) pgpFact.nextObject();
|
o = pgpFact.nextObject();
|
||||||
} else if (o instanceof PGPSignatureList) {
|
}
|
||||||
sigList = (PGPSignatureList) o;
|
|
||||||
} else {
|
PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mProviderHelper);
|
||||||
|
|
||||||
|
if ( ! signatureChecker.initializeSignature(o, log, indent+1)) {
|
||||||
log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0);
|
log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0);
|
||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent);
|
if (signatureChecker.isInitialized()) {
|
||||||
|
|
||||||
if (signature != null) {
|
|
||||||
updateProgress(R.string.progress_reading_data, 60, 100);
|
updateProgress(R.string.progress_reading_data, 60, 100);
|
||||||
|
|
||||||
ProgressScaler progressScaler = new ProgressScaler(mProgressable, 60, 90, 100);
|
ProgressScaler progressScaler = new ProgressScaler(mProgressable, 60, 90, 100);
|
||||||
@@ -1098,7 +837,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update signature buffer if signature is also present
|
// update signature buffer if signature is also present
|
||||||
signature.update(buffer, 0, length);
|
signatureChecker.updateSignatureData(buffer, 0, length);
|
||||||
|
|
||||||
alreadyWritten += length;
|
alreadyWritten += length;
|
||||||
if (wholeSize > 0) {
|
if (wholeSize > 0) {
|
||||||
@@ -1109,105 +848,28 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
}
|
}
|
||||||
progressScaler.setProgress((int) progress, 100);
|
progressScaler.setProgress((int) progress, 100);
|
||||||
}
|
}
|
||||||
// TODO: slow annealing to fake a progress?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProgress(R.string.progress_verifying_signature, 90, 100);
|
updateProgress(R.string.progress_verifying_signature, 90, 100);
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
|
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
|
||||||
|
|
||||||
// Verify signature and check binding signatures
|
signatureChecker.verifySignature(log, indent);
|
||||||
boolean validSignature = signature.verify();
|
|
||||||
if (validSignature) {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
|
|
||||||
} else {
|
|
||||||
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for insecure hash algorithms
|
|
||||||
if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
|
|
||||||
log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
|
|
||||||
signatureResultBuilder.setInsecure(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
signatureResultBuilder.setValidSignature(validSignature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
|
||||||
log.add(LogType.MSG_DC_OK, indent);
|
log.add(LogType.MSG_DC_OK, indent);
|
||||||
|
|
||||||
|
// TODO return metadata object?
|
||||||
|
|
||||||
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
||||||
result.setSignatureResult(signatureResultBuilder.build());
|
result.setSignatureResult(signatureChecker.getSignatureResult());
|
||||||
result.setDecryptionResult(
|
result.setDecryptionResult(
|
||||||
new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
|
new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPSignature processPGPSignatureList(
|
|
||||||
PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder,
|
|
||||||
OperationLog log, int indent)
|
|
||||||
throws PGPException {
|
|
||||||
CanonicalizedPublicKeyRing signingRing = null;
|
|
||||||
CanonicalizedPublicKey signingKey = null;
|
|
||||||
int signatureIndex = -1;
|
|
||||||
|
|
||||||
// go through all signatures
|
|
||||||
// and find out for which signature we have a key in our database
|
|
||||||
for (int i = 0; i < sigList.size(); ++i) {
|
|
||||||
try {
|
|
||||||
long sigKeyId = sigList.get(i).getKeyID();
|
|
||||||
signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
|
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
|
|
||||||
);
|
|
||||||
signingKey = signingRing.getPublicKey(sigKeyId);
|
|
||||||
signatureIndex = i;
|
|
||||||
} catch (ProviderHelper.NotFoundException e) {
|
|
||||||
Log.d(Constants.TAG, "key not found, trying next signature...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPSignature signature = null;
|
|
||||||
|
|
||||||
if (signingKey != null) {
|
|
||||||
// key found in our database!
|
|
||||||
signature = sigList.get(signatureIndex);
|
|
||||||
|
|
||||||
signatureResultBuilder.initValid(signingRing, signingKey);
|
|
||||||
|
|
||||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
|
||||||
new JcaPGPContentVerifierBuilderProvider()
|
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
|
||||||
signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
|
|
||||||
} else {
|
|
||||||
// no key in our database -> return "unknown pub key" status including the first key id
|
|
||||||
if (!sigList.isEmpty()) {
|
|
||||||
signatureResultBuilder.setSignatureAvailable(true);
|
|
||||||
signatureResultBuilder.setKnownKey(false);
|
|
||||||
signatureResultBuilder.setKeyId(sigList.get(0).getKeyID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for insecure signing key
|
|
||||||
// TODO: checks on signingRing ?
|
|
||||||
if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) {
|
|
||||||
log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
|
|
||||||
signatureResultBuilder.setInsecure(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
|
|
||||||
*/
|
|
||||||
private static void processLine(PGPSignature sig, byte[] line)
|
|
||||||
throws SignatureException {
|
|
||||||
int length = getLengthWithoutWhiteSpace(line);
|
|
||||||
if (length > 0) {
|
|
||||||
sig.update(line, 0, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
|
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
bOut.reset();
|
bOut.reset();
|
||||||
@@ -1273,22 +935,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
return b == '\r' || b == '\n';
|
return b == '\r' || b == '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getLengthWithoutWhiteSpace(byte[] line) {
|
|
||||||
int end = line.length - 1;
|
|
||||||
|
|
||||||
while (end >= 0 && isWhiteSpace(line[end])) {
|
|
||||||
end--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return end + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isWhiteSpace(byte b) {
|
|
||||||
return b == '\r' || b == '\n' || b == '\t' || b == ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getLineSeparator() {
|
private static byte[] getLineSeparator() {
|
||||||
String nl = System.getProperty("line.separator");
|
String nl = System.getProperty("line.separator");
|
||||||
return nl.getBytes();
|
return nl.getBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -491,9 +491,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
|||||||
|
|
||||||
literalGen.close();
|
literalGen.close();
|
||||||
} else {
|
} else {
|
||||||
pOut = null;
|
throw new AssertionError("cannot clearsign in non-ascii armored text, this is a bug!");
|
||||||
// TODO: Is this log right?
|
|
||||||
log.add(LogType.MSG_PSE_CLEARSIGN_ONLY, indent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableSignature) {
|
if (enableSignature) {
|
||||||
|
|||||||
@@ -0,0 +1,344 @@
|
|||||||
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
|
||||||
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
|
import org.spongycastle.openpgp.PGPException;
|
||||||
|
import org.spongycastle.openpgp.PGPOnePassSignature;
|
||||||
|
import org.spongycastle.openpgp.PGPOnePassSignatureList;
|
||||||
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
|
import org.spongycastle.openpgp.PGPSignatureList;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
|
||||||
|
/** This class is used to track the state of a single signature verification.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class PgpSignatureChecker {
|
||||||
|
|
||||||
|
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
|
||||||
|
|
||||||
|
private CanonicalizedPublicKey signingKey;
|
||||||
|
|
||||||
|
private int signatureIndex;
|
||||||
|
PGPOnePassSignature onePassSignature;
|
||||||
|
PGPSignature signature;
|
||||||
|
|
||||||
|
ProviderHelper mProviderHelper;
|
||||||
|
|
||||||
|
PgpSignatureChecker(ProviderHelper providerHelper) {
|
||||||
|
mProviderHelper = providerHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean initializeSignature(Object dataChunk, OperationLog log, int indent) throws PGPException {
|
||||||
|
|
||||||
|
if (!(dataChunk instanceof PGPSignatureList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPSignatureList sigList = (PGPSignatureList) dataChunk;
|
||||||
|
findAvailableSignature(sigList);
|
||||||
|
|
||||||
|
if (signingKey != null) {
|
||||||
|
|
||||||
|
// key found in our database!
|
||||||
|
signatureResultBuilder.initValid(signingKey);
|
||||||
|
|
||||||
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||||
|
new JcaPGPContentVerifierBuilderProvider()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
|
||||||
|
checkKeySecurity(log, indent);
|
||||||
|
|
||||||
|
|
||||||
|
} else if (!sigList.isEmpty()) {
|
||||||
|
|
||||||
|
signatureResultBuilder.setSignatureAvailable(true);
|
||||||
|
signatureResultBuilder.setKnownKey(false);
|
||||||
|
signatureResultBuilder.setKeyId(sigList.get(0).getKeyID());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean initializeOnePassSignature(Object dataChunk, OperationLog log, int indent) throws PGPException {
|
||||||
|
|
||||||
|
if (!(dataChunk instanceof PGPOnePassSignatureList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent + 1);
|
||||||
|
|
||||||
|
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
|
||||||
|
findAvailableSignature(sigList);
|
||||||
|
|
||||||
|
if (signingKey != null) {
|
||||||
|
|
||||||
|
// key found in our database!
|
||||||
|
signatureResultBuilder.initValid(signingKey);
|
||||||
|
|
||||||
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||||
|
new JcaPGPContentVerifierBuilderProvider()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
onePassSignature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
|
||||||
|
|
||||||
|
checkKeySecurity(log, indent);
|
||||||
|
|
||||||
|
} else if (!sigList.isEmpty()) {
|
||||||
|
|
||||||
|
signatureResultBuilder.setSignatureAvailable(true);
|
||||||
|
signatureResultBuilder.setKnownKey(false);
|
||||||
|
signatureResultBuilder.setKeyId(sigList.get(0).getKeyID());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkKeySecurity(OperationLog log, int indent) {
|
||||||
|
// TODO: checks on signingRing ?
|
||||||
|
if (!PgpSecurityConstants.isSecureKey(signingKey)) {
|
||||||
|
log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
|
||||||
|
signatureResultBuilder.setInsecure(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return signingKey != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findAvailableSignature(PGPOnePassSignatureList sigList) {
|
||||||
|
// go through all signatures (should be just one), make sure we have
|
||||||
|
// the key and it matches the one we’re looking for
|
||||||
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
|
try {
|
||||||
|
long sigKeyId = sigList.get(i).getKeyID();
|
||||||
|
CanonicalizedPublicKeyRing signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
|
||||||
|
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
|
||||||
|
);
|
||||||
|
CanonicalizedPublicKey keyCandidate = signingRing.getPublicKey(sigKeyId);
|
||||||
|
if ( ! signingKey.canSign()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
signatureIndex = i;
|
||||||
|
signingKey = keyCandidate;
|
||||||
|
onePassSignature = sigList.get(i);
|
||||||
|
return;
|
||||||
|
} catch (ProviderHelper.NotFoundException e) {
|
||||||
|
Log.d(Constants.TAG, "key not found, trying next signature...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void findAvailableSignature(PGPSignatureList sigList) {
|
||||||
|
// go through all signatures (should be just one), make sure we have
|
||||||
|
// the key and it matches the one we’re looking for
|
||||||
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
|
try {
|
||||||
|
long sigKeyId = sigList.get(i).getKeyID();
|
||||||
|
CanonicalizedPublicKeyRing signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
|
||||||
|
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
|
||||||
|
);
|
||||||
|
CanonicalizedPublicKey keyCandidate = signingRing.getPublicKey(sigKeyId);
|
||||||
|
if ( ! signingKey.canSign()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
signatureIndex = i;
|
||||||
|
signingKey = keyCandidate;
|
||||||
|
signature = sigList.get(i);
|
||||||
|
return;
|
||||||
|
} catch (ProviderHelper.NotFoundException e) {
|
||||||
|
Log.d(Constants.TAG, "key not found, trying next signature...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSignatureWithCleartext(byte[] clearText) throws IOException, SignatureException {
|
||||||
|
|
||||||
|
InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText));
|
||||||
|
|
||||||
|
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
int lookAhead = readInputLine(outputBuffer, sigIn);
|
||||||
|
|
||||||
|
processLine(signature, outputBuffer.toByteArray());
|
||||||
|
|
||||||
|
while (lookAhead != -1) {
|
||||||
|
lookAhead = readInputLine(outputBuffer, lookAhead, sigIn);
|
||||||
|
|
||||||
|
signature.update((byte) '\r');
|
||||||
|
signature.update((byte) '\n');
|
||||||
|
|
||||||
|
processLine(signature, outputBuffer.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSignatureData(byte[] buf, int off, int len) {
|
||||||
|
if (signature != null) {
|
||||||
|
signature.update(buf, off, len);
|
||||||
|
} else if (onePassSignature != null) {
|
||||||
|
onePassSignature.update(buf, off, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifySignature(OperationLog log, int indent) throws PGPException {
|
||||||
|
|
||||||
|
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
|
||||||
|
|
||||||
|
// Verify signature
|
||||||
|
boolean validSignature = signature.verify();
|
||||||
|
if (validSignature) {
|
||||||
|
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
|
||||||
|
} else {
|
||||||
|
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for insecure hash algorithms
|
||||||
|
if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
|
||||||
|
log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
|
||||||
|
signatureResultBuilder.setInsecure(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
signatureResultBuilder.setValidSignature(validSignature);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean verifySignatureOnePass(Object o, OperationLog log, int indent) throws PGPException {
|
||||||
|
|
||||||
|
if (!(o instanceof PGPSignatureList)) {
|
||||||
|
log.add(LogType.MSG_DC_ERROR_NO_SIGNATURE, indent);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PGPSignatureList signatureList = (PGPSignatureList) o;
|
||||||
|
if (signatureList.size() <= signatureIndex) {
|
||||||
|
log.add(LogType.MSG_DC_ERROR_NO_SIGNATURE, indent);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PGPOnePassSignature and PGPSignature packets are "bracketed",
|
||||||
|
// so we need to take the last-minus-index'th element here
|
||||||
|
PGPSignature messageSignature = signatureList.get(signatureList.size() - 1 - signatureIndex);
|
||||||
|
|
||||||
|
// Verify signature
|
||||||
|
boolean validSignature = onePassSignature.verify(messageSignature);
|
||||||
|
if (validSignature) {
|
||||||
|
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
|
||||||
|
} else {
|
||||||
|
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for insecure hash algorithms
|
||||||
|
if (!PgpSecurityConstants.isSecureHashAlgorithm(onePassSignature.getHashAlgorithm())) {
|
||||||
|
log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
|
||||||
|
signatureResultBuilder.setInsecure(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
signatureResultBuilder.setValidSignature(validSignature);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSigningFingerprint() {
|
||||||
|
return signingKey.getFingerprint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenPgpSignatureResult getSignatureResult() {
|
||||||
|
return signatureResultBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static void processLine(PGPSignature sig, byte[] line)
|
||||||
|
throws SignatureException {
|
||||||
|
int length = getLengthWithoutWhiteSpace(line);
|
||||||
|
if (length > 0) {
|
||||||
|
sig.update(line, 0, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
|
||||||
|
throws IOException {
|
||||||
|
bOut.reset();
|
||||||
|
|
||||||
|
int lookAhead = -1;
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
while ((ch = fIn.read()) >= 0) {
|
||||||
|
bOut.write(ch);
|
||||||
|
if (ch == '\r' || ch == '\n') {
|
||||||
|
lookAhead = readPastEOL(bOut, ch, fIn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookAhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn)
|
||||||
|
throws IOException {
|
||||||
|
bOut.reset();
|
||||||
|
|
||||||
|
int ch = lookAhead;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bOut.write(ch);
|
||||||
|
if (ch == '\r' || ch == '\n') {
|
||||||
|
lookAhead = readPastEOL(bOut, ch, fIn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while ((ch = fIn.read()) >= 0);
|
||||||
|
|
||||||
|
if (ch < 0) {
|
||||||
|
lookAhead = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookAhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readPastEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
|
||||||
|
throws IOException {
|
||||||
|
int lookAhead = fIn.read();
|
||||||
|
|
||||||
|
if (lastCh == '\r' && lookAhead == '\n') {
|
||||||
|
bOut.write(lookAhead);
|
||||||
|
lookAhead = fIn.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookAhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getLengthWithoutWhiteSpace(byte[] line) {
|
||||||
|
int end = line.length - 1;
|
||||||
|
|
||||||
|
while (end >= 0 && isWhiteSpace(line[end])) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isWhiteSpace(byte b) {
|
||||||
|
return b == '\r' || b == '\n' || b == '\t' || b == ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -436,7 +436,6 @@
|
|||||||
<string name="progress_decrypting">"decrypting data…"</string>
|
<string name="progress_decrypting">"decrypting data…"</string>
|
||||||
<string name="progress_preparing_signature">"preparing signature…"</string>
|
<string name="progress_preparing_signature">"preparing signature…"</string>
|
||||||
<string name="progress_generating_signature">"generating signature…"</string>
|
<string name="progress_generating_signature">"generating signature…"</string>
|
||||||
<string name="progress_processing_signature">"processing signature…"</string>
|
|
||||||
<string name="progress_verifying_signature">"verifying signature…"</string>
|
<string name="progress_verifying_signature">"verifying signature…"</string>
|
||||||
<string name="progress_signing">"signing…"</string>
|
<string name="progress_signing">"signing…"</string>
|
||||||
<string name="progress_certifying">"certifying…"</string>
|
<string name="progress_certifying">"certifying…"</string>
|
||||||
@@ -1174,6 +1173,7 @@
|
|||||||
<string name="msg_dc_error_input">"Error opening input data stream!"</string>
|
<string name="msg_dc_error_input">"Error opening input data stream!"</string>
|
||||||
<string name="msg_dc_error_no_data">"No encrypted data found in stream!"</string>
|
<string name="msg_dc_error_no_data">"No encrypted data found in stream!"</string>
|
||||||
<string name="msg_dc_error_no_key">"No encrypted data with known secret key found in stream!"</string>
|
<string name="msg_dc_error_no_key">"No encrypted data with known secret key found in stream!"</string>
|
||||||
|
<string name="msg_dc_error_no_signature">"Missing signature data!"</string>
|
||||||
<string name="msg_dc_error_pgp_exception">"Encountered OpenPGP Exception during operation!"</string>
|
<string name="msg_dc_error_pgp_exception">"Encountered OpenPGP Exception during operation!"</string>
|
||||||
<string name="msg_dc_integrity_check_ok">"Integrity check OK!"</string>
|
<string name="msg_dc_integrity_check_ok">"Integrity check OK!"</string>
|
||||||
<string name="msg_dc_ok_meta_only">"Only metadata was requested, skipping decryption"</string>
|
<string name="msg_dc_ok_meta_only">"Only metadata was requested, skipping decryption"</string>
|
||||||
@@ -1197,8 +1197,9 @@
|
|||||||
|
|
||||||
<!-- Messages for VerifySignedLiteralData operation -->
|
<!-- Messages for VerifySignedLiteralData operation -->
|
||||||
<string name="msg_vl">"Starting signature check"</string>
|
<string name="msg_vl">"Starting signature check"</string>
|
||||||
<string name="msg_vl_error_no_siglist">"No signature list in signed literal data"</string>
|
<string name="msg_vl_error_no_siglist">"No signature list in signed literal data!"</string>
|
||||||
<string name="msg_vl_error_wrong_key">"Message not signed with right key"</string>
|
<string name="msg_vl_error_wrong_key">"Message not signed with expected key!"</string>
|
||||||
|
<string name="msg_vl_error_no_signature">"Missing signature data!"</string>
|
||||||
<string name="msg_vl_error_missing_literal">"No payload in signed literal data"</string>
|
<string name="msg_vl_error_missing_literal">"No payload in signed literal data"</string>
|
||||||
<string name="msg_vl_clear_meta_file">"Filename: %s"</string>
|
<string name="msg_vl_clear_meta_file">"Filename: %s"</string>
|
||||||
<string name="msg_vl_clear_meta_mime">"MIME type: %s"</string>
|
<string name="msg_vl_clear_meta_mime">"MIME type: %s"</string>
|
||||||
@@ -1220,7 +1221,6 @@
|
|||||||
|
|
||||||
<!-- Messages for PgpSignEncrypt operation -->
|
<!-- Messages for PgpSignEncrypt operation -->
|
||||||
<string name="msg_pse_asymmetric">"Preparing public keys for encryption"</string>
|
<string name="msg_pse_asymmetric">"Preparing public keys for encryption"</string>
|
||||||
<string name="msg_pse_clearsign_only">"Signing of cleartext input not supported!"</string>
|
|
||||||
<string name="msg_pse_compressing">"Preparing compression"</string>
|
<string name="msg_pse_compressing">"Preparing compression"</string>
|
||||||
<string name="msg_pse_encrypting">"Encrypting data"</string>
|
<string name="msg_pse_encrypting">"Encrypting data"</string>
|
||||||
<string name="msg_pse_error_bad_passphrase">"Bad password!"</string>
|
<string name="msg_pse_error_bad_passphrase">"Bad password!"</string>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import java.util.Date;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.apache.tools.ant.util.StringUtils;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
@@ -287,7 +288,7 @@ public class PgpEncryptDecryptTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAsymmetricSign() {
|
public void testAsymmetricSignLiteral() {
|
||||||
|
|
||||||
String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
|
String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
|
||||||
byte[] ciphertext;
|
byte[] ciphertext;
|
||||||
@@ -340,6 +341,121 @@ public class PgpEncryptDecryptTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAsymmetricSignCleartext() {
|
||||||
|
|
||||||
|
String plaintext = "dies ist ein\r\nplaintext\n ☭" + TestingUtils.genPassphrase(true);
|
||||||
|
byte[] ciphertext;
|
||||||
|
|
||||||
|
{ // encrypt data with key
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
|
||||||
|
|
||||||
|
PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
|
||||||
|
new ProviderHelper(RuntimeEnvironment.application), null);
|
||||||
|
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel();
|
||||||
|
|
||||||
|
// only sign, as cleartext
|
||||||
|
input.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId());
|
||||||
|
input.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
|
||||||
|
input.setCleartextSignature(true);
|
||||||
|
input.setEnableAsciiArmorOutput(true);
|
||||||
|
input.setDetachedSignature(false);
|
||||||
|
|
||||||
|
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out);
|
||||||
|
Assert.assertTrue("signing must succeed", result.success());
|
||||||
|
|
||||||
|
ciphertext = out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertTrue("clearsigned text must contain plaintext (ignoring newlines)",
|
||||||
|
new String(ciphertext).replace("\r\n", "").contains(plaintext.replace("\r", "").replace("\n", "")));
|
||||||
|
|
||||||
|
{ // verification should succeed
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
|
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
|
||||||
|
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
|
||||||
|
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
|
||||||
|
|
||||||
|
Assert.assertTrue("verification must succeed", result.success());
|
||||||
|
|
||||||
|
Assert.assertTrue("verification text should equal plaintext (ignoring newlines)",
|
||||||
|
new String(out.toByteArray()).replace(StringUtils.LINE_SEP, "")
|
||||||
|
.equals(plaintext.replace("\r", "").replace("\n", "")));
|
||||||
|
Assert.assertEquals("decryptionResult should be RESULT_NOT_ENCRYPTED",
|
||||||
|
OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED, result.getDecryptionResult().getResult());
|
||||||
|
Assert.assertEquals("signatureResult should be RESULT_VALID_CONFIRMED",
|
||||||
|
OpenPgpSignatureResult.RESULT_VALID_CONFIRMED, result.getSignatureResult().getResult());
|
||||||
|
|
||||||
|
OpenPgpMetadata metadata = result.getDecryptionMetadata();
|
||||||
|
Assert.assertEquals("filesize must be correct",
|
||||||
|
out.toByteArray().length, metadata.getOriginalSize());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAsymmetricSignDetached() {
|
||||||
|
|
||||||
|
String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
|
||||||
|
byte[] detachedSignature;
|
||||||
|
|
||||||
|
{ // encrypt data with key
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
|
||||||
|
|
||||||
|
PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
|
||||||
|
new ProviderHelper(RuntimeEnvironment.application), null);
|
||||||
|
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel();
|
||||||
|
|
||||||
|
// only sign, as cleartext
|
||||||
|
input.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId());
|
||||||
|
input.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
|
||||||
|
input.setDetachedSignature(true);
|
||||||
|
|
||||||
|
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out);
|
||||||
|
Assert.assertTrue("signing must succeed", result.success());
|
||||||
|
|
||||||
|
detachedSignature = result.getDetachedSignature();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // verification should succeed
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
|
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
|
||||||
|
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
|
||||||
|
input.setDetachedSignature(detachedSignature);
|
||||||
|
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
|
||||||
|
|
||||||
|
Assert.assertTrue("verification must succeed", result.success());
|
||||||
|
Assert.assertArrayEquals("verification text should equal plaintext (save for a newline)",
|
||||||
|
plaintext.getBytes(), out.toByteArray());
|
||||||
|
Assert.assertEquals("decryptionResult should be RESULT_NOT_ENCRYPTED",
|
||||||
|
OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED, result.getDecryptionResult().getResult());
|
||||||
|
Assert.assertEquals("signatureResult should be RESULT_VALID_CONFIRMED",
|
||||||
|
OpenPgpSignatureResult.RESULT_VALID_CONFIRMED, result.getSignatureResult().getResult());
|
||||||
|
|
||||||
|
// TODO should detached verify return any metadata?
|
||||||
|
// OpenPgpMetadata metadata = result.getDecryptionMetadata();
|
||||||
|
// Assert.assertEquals("filesize must be correct",
|
||||||
|
// out.toByteArray().length, metadata.getOriginalSize());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAsymmetricEncryptDecrypt() {
|
public void testAsymmetricEncryptDecrypt() {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user