Merge pull request #643 from open-keychain/v/dev

wrap bouncycastle objects and don't use them in android-related code
This commit is contained in:
Dominik Schürmann 2014-06-03 13:06:49 +02:00
commit 84d03dd514
57 changed files with 2734 additions and 1797 deletions

View File

@ -1,6 +1,7 @@
# Design Notes on Open-Keychain # Design Notes on Open-Keychain
This document contains notes on the software design of open keychain. This document contains notes on the software design of open keychain. Points
with a * are yet to be implemented.
## Database design ## Database design

View File

@ -1,9 +1,11 @@
apply plugin: 'android' apply plugin: 'android'
apply plugin: 'android-test'
sourceSets { sourceSets {
testLocal { androidTest {
java.srcDir file('src/test/java') java.srcDir file('src/test/java')
resources.srcDir file('src/test/resources') // configure the set of classes for JUnit tests
// include '**/*Test.class'
} }
} }
@ -23,24 +25,25 @@ dependencies {
compile project(':extern:spongycastle:prov') compile project(':extern:spongycastle:prov')
compile project(':extern:AppMsg:library') compile project(':extern:AppMsg:library')
// Dependencies for the `testLocal` task, make sure to list all your global dependencies here as well // Dependencies for the `instrumentTest` task, make sure to list all your global dependencies here as well
testLocalCompile 'junit:junit:4.11' androidTestCompile 'junit:junit:4.10'
testLocalCompile 'org.robolectric:robolectric:2.2' androidTestCompile 'org.robolectric:robolectric:2.1.+'
testLocalCompile 'com.google.android:android:4.1.1.4' androidTestCompile 'com.squareup:fest-android:1.0.+'
testLocalCompile 'com.android.support:support-v4:19.1.0' androidTestCompile 'com.google.android:android:4.1.1.4'
testLocalCompile 'com.android.support:appcompat-v7:19.1.0' androidTestCompile 'com.android.support:support-v4:19.1.0'
testLocalCompile project(':extern:openpgp-api-lib') androidTestCompile 'com.android.support:appcompat-v7:19.1.0'
testLocalCompile project(':extern:openkeychain-api-lib') androidTestCompile project(':extern:openpgp-api-lib')
testLocalCompile project(':extern:html-textview') androidTestCompile project(':extern:openkeychain-api-lib')
testLocalCompile project(':extern:StickyListHeaders:library') androidTestCompile project(':extern:html-textview')
testLocalCompile project(':extern:AndroidBootstrap:AndroidBootstrap') androidTestCompile project(':extern:StickyListHeaders:library')
testLocalCompile project(':extern:zxing-qr-code') androidTestCompile project(':extern:AndroidBootstrap:AndroidBootstrap')
testLocalCompile project(':extern:zxing-android-integration') androidTestCompile project(':extern:zxing-qr-code')
testLocalCompile project(':extern:spongycastle:core') androidTestCompile project(':extern:zxing-android-integration')
testLocalCompile project(':extern:spongycastle:pg') androidTestCompile project(':extern:spongycastle:core')
testLocalCompile project(':extern:spongycastle:pkix') androidTestCompile project(':extern:spongycastle:pg')
testLocalCompile project(':extern:spongycastle:prov') androidTestCompile project(':extern:spongycastle:pkix')
testLocalCompile project(':extern:AppMsg:library') androidTestCompile project(':extern:spongycastle:prov')
androidTestCompile project(':extern:AppMsg:library')
} }
android { android {
@ -92,20 +95,6 @@ android {
} }
} }
task localTest(type: Test, dependsOn: assemble) {
testClassesDir = sourceSets.testLocal.output.classesDir
android.sourceSets.main.java.srcDirs.each { dir ->
def buildDir = dir.getAbsolutePath().split("\\" + File.separator)
buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join("\\" + File.separator)
sourceSets.testLocal.compileClasspath += files(buildDir)
sourceSets.testLocal.runtimeClasspath += files(buildDir)
}
classpath = sourceSets.testLocal.runtimeClasspath
}
// NOTE: This disables Lint! // NOTE: This disables Lint!
tasks.whenTaskAdded { task -> tasks.whenTaskAdded { task ->
if (task.name.equals("lint")) { if (task.name.equals("lint")) {
@ -113,5 +102,3 @@ tasks.whenTaskAdded { task ->
} }
} }
// NOTE: tests disabled!
//check.dependsOn localTest

View File

@ -0,0 +1,12 @@
package tests;
import org.junit.Assert;
import org.junit.Test;
public class SomeTest {
@Test
public void willFail() {
// stub
// Assert.assertThat();
}
}

View File

@ -31,6 +31,7 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@ -51,14 +52,15 @@ public class ExportHelper {
public void deleteKey(Uri dataUri, Handler deleteHandler) { public void deleteKey(Uri dataUri, Handler deleteHandler) {
try { try {
long masterKeyId = new ProviderHelper(mActivity).extractOrGetMasterKeyId(dataUri); long masterKeyId = new ProviderHelper(mActivity).getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
// Create a new Messenger for the communication back // Create a new Messenger for the communication back
Messenger messenger = new Messenger(deleteHandler); Messenger messenger = new Messenger(deleteHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
new long[]{ masterKeyId }); new long[]{ masterKeyId });
deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog"); deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
} catch (ProviderHelper.NotFoundException e) { } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
} }
} }

View File

@ -21,18 +21,10 @@ import android.content.Context;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@ -55,8 +47,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
private boolean mSelected; private boolean mSelected;
private byte[] mBytes = new byte[]{};
public int describeContents() { public int describeContents() {
return 0; return 0;
} }
@ -74,8 +64,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
dest.writeString(algorithm); dest.writeString(algorithm);
dest.writeByte((byte) (secretKey ? 1 : 0)); dest.writeByte((byte) (secretKey ? 1 : 0));
dest.writeByte((byte) (mSelected ? 1 : 0)); dest.writeByte((byte) (mSelected ? 1 : 0));
dest.writeInt(mBytes.length);
dest.writeByteArray(mBytes);
dest.writeString(mExtraData); dest.writeString(mExtraData);
} }
@ -94,8 +82,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.algorithm = source.readString(); vr.algorithm = source.readString();
vr.secretKey = source.readByte() == 1; vr.secretKey = source.readByte() == 1;
vr.mSelected = source.readByte() == 1; vr.mSelected = source.readByte() == 1;
vr.mBytes = new byte[source.readInt()];
source.readByteArray(vr.mBytes);
vr.mExtraData = source.readString(); vr.mExtraData = source.readString();
return vr; return vr;
@ -110,14 +96,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
return keyIdHex; return keyIdHex;
} }
public byte[] getBytes() {
return mBytes;
}
public void setBytes(byte[] bytes) {
this.mBytes = bytes;
}
public boolean isSelected() { public boolean isSelected() {
return mSelected; return mSelected;
} }
@ -233,53 +211,25 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
* Constructor based on key object, used for import from NFC, QR Codes, files * Constructor based on key object, used for import from NFC, QR Codes, files
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public ImportKeysListEntry(Context context, PGPKeyRing pgpKeyRing) { public ImportKeysListEntry(Context context, UncachedKeyRing ring) {
// save actual key object into entry, used to import it later
try {
this.mBytes = pgpKeyRing.getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "IOException on pgpKeyRing.getEncoded()", e);
}
// selected is default // selected is default
this.mSelected = true; this.mSelected = true;
if (pgpKeyRing instanceof PGPSecretKeyRing) { secretKey = ring.isSecret();
secretKey = true; UncachedPublicKey key = ring.getPublicKey();
} else {
secretKey = false;
}
PGPPublicKey key = pgpKeyRing.getPublicKey();
userIds = new ArrayList<String>(); mPrimaryUserId = key.getPrimaryUserId();
for (String userId : new IterableIterator<String>(key.getUserIDs())) { userIds = key.getUnorderedUserIds();
userIds.add(userId);
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignaturesForID(userId))) {
if (sig.getHashedSubPackets() != null
&& sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
try {
// make sure it's actually valid
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), key);
if (sig.verifyCertification(userId, key)) {
mPrimaryUserId = userId;
}
} catch (Exception e) {
// nothing bad happens, the key is just not considered the primary key id
}
}
}
}
// if there was no user id flagged as primary, use the first one // if there was no user id flagged as primary, use the first one
if (mPrimaryUserId == null) { if (mPrimaryUserId == null) {
mPrimaryUserId = userIds.get(0); mPrimaryUserId = userIds.get(0);
} }
this.keyId = key.getKeyID(); this.keyId = key.getKeyId();
this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId); this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
this.revoked = key.isRevoked(); this.revoked = key.maybeRevoked();
this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
this.bitStrength = key.getBitStrength(); this.bitStrength = key.getBitStrength();
final int algorithm = key.getAlgorithm(); final int algorithm = key.getAlgorithm();

View File

@ -0,0 +1,51 @@
package org.sufficientlysecure.keychain.keyimport;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import java.io.IOException;
/** This is a trivial wrapper around UncachedKeyRing which implements Parcelable. It exists
* for the sole purpose of keeping spongycastle and android imports in separate packages.
*/
public class ParcelableKeyRing implements Parcelable {
final byte[] mBytes;
final String mExpectedFingerprint;
public ParcelableKeyRing(byte[] bytes) {
mBytes = bytes;
mExpectedFingerprint = null;
}
public ParcelableKeyRing(byte[] bytes, String expectedFingerprint) {
mBytes = bytes;
mExpectedFingerprint = expectedFingerprint;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeByteArray(mBytes);
dest.writeString(mExpectedFingerprint);
}
public static final Creator<ParcelableKeyRing> CREATOR = new Creator<ParcelableKeyRing>() {
public ParcelableKeyRing createFromParcel(final Parcel source) {
return new ParcelableKeyRing(source.createByteArray());
}
public ParcelableKeyRing[] newArray(final int size) {
return new ParcelableKeyRing[size];
}
};
public int describeContents() {
return 0;
}
public UncachedKeyRing getUncachedKeyRing() throws PgpGeneralException, IOException {
return UncachedKeyRing.decodeFromData(mBytes);
}
}

View File

@ -0,0 +1,36 @@
package org.sufficientlysecure.keychain.pgp;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
/** An abstract KeyRing.
*
* This is an abstract class for all KeyRing constructs. It serves as a common
* denominator of available information, two implementations wrapping the same
* keyring should in all cases agree on the output of all methods described
* here.
*
* @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
* @see org.sufficientlysecure.keychain.provider.CachedPublicKeyRing
*
*/
public abstract class KeyRing {
abstract public long getMasterKeyId() throws PgpGeneralException;
abstract public String getPrimaryUserId() throws PgpGeneralException;
abstract public boolean isRevoked() throws PgpGeneralException;
abstract public boolean canCertify() throws PgpGeneralException;
abstract public long getEncryptId() throws PgpGeneralException;
abstract public boolean hasEncrypt() throws PgpGeneralException;
abstract public long getSignId() throws PgpGeneralException;
abstract public boolean hasSign() throws PgpGeneralException;
abstract public int getVerified() throws PgpGeneralException;
}

View File

@ -21,49 +21,25 @@ import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
public class PgpConversionHelper { public class PgpConversionHelper {
/**
* Convert from byte[] to PGPKeyRing
*
* @param keysBytes
* @return
*/
public static PGPKeyRing BytesToPGPKeyRing(byte[] keysBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
PGPKeyRing keyRing = null;
try {
if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
Log.e(Constants.TAG, "No keys given!");
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
}
return keyRing;
}
/** /**
* Convert from byte[] to ArrayList<PGPSecretKey> * Convert from byte[] to ArrayList<PGPSecretKey>
* *
* @param keysBytes * @param keysBytes
* @return * @return
*/ */
public static ArrayList<PGPSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) { public static ArrayList<UncachedSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keysBytes); PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
Object obj = null; Object obj = null;
ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>(); ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
try { try {
while ((obj = factory.nextObject()) != null) { while ((obj = factory.nextObject()) != null) {
PGPSecretKey secKey = null; PGPSecretKey secKey = null;
@ -72,7 +48,7 @@ public class PgpConversionHelper {
if (secKey == null) { if (secKey == null) {
Log.e(Constants.TAG, "No keys given!"); Log.e(Constants.TAG, "No keys given!");
} }
keys.add(secKey); keys.add(new UncachedSecretKey(secKey));
} else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings } else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
PGPSecretKeyRing keyRing = null; PGPSecretKeyRing keyRing = null;
keyRing = (PGPSecretKeyRing) obj; keyRing = (PGPSecretKeyRing) obj;
@ -82,7 +58,7 @@ public class PgpConversionHelper {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Iterator<PGPSecretKey> itr = keyRing.getSecretKeys(); Iterator<PGPSecretKey> itr = keyRing.getSecretKeys();
while (itr.hasNext()) { while (itr.hasNext()) {
keys.add(itr.next()); keys.add(new UncachedSecretKey(itr.next()));
} }
} }
} }
@ -100,7 +76,7 @@ public class PgpConversionHelper {
* @param keyBytes * @param keyBytes
* @return * @return
*/ */
public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) { public static UncachedSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keyBytes); PGPObjectFactory factory = new PGPObjectFactory(keyBytes);
Object obj = null; Object obj = null;
try { try {
@ -121,80 +97,7 @@ public class PgpConversionHelper {
secKey = keyRing.getSecretKey(); secKey = keyRing.getSecretKey();
} }
return secKey; return new UncachedSecretKey(secKey);
}
/**
* Convert from byte[] to PGPSignature
*
* @param sigBytes
* @return
*/
public static PGPSignature BytesToPGPSignature(byte[] sigBytes) {
PGPObjectFactory factory = new PGPObjectFactory(sigBytes);
PGPSignatureList signatures = null;
try {
if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) {
Log.e(Constants.TAG, "No signatures given!");
return null;
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPSignature!", e);
return null;
}
return signatures.get(0);
}
/**
* Convert from ArrayList<PGPSecretKey> to byte[]
*
* @param keys
* @return
*/
public static byte[] PGPSecretKeyArrayListToBytes(ArrayList<PGPSecretKey> keys) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
for (PGPSecretKey key : keys) {
try {
key.encode(os);
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting ArrayList<PGPSecretKey> to byte[]!", e);
}
}
return os.toByteArray();
}
/**
* Convert from PGPSecretKey to byte[]
*
* @param key
* @return
*/
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
try {
return key.getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "Encoding failed", e);
return null;
}
}
/**
* Convert from PGPSecretKeyRing to byte[]
*
* @param keyRing
* @return
*/
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
try {
return keyRing.getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "Encoding failed", e);
return null;
}
} }
} }

View File

@ -18,10 +18,7 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import android.net.Uri;
import org.spongycastle.bcpg.ArmoredInputStream; import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPCompressedData; import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataList; import org.spongycastle.openpgp.PGPEncryptedDataList;
@ -31,29 +28,19 @@ import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPOnePassSignature; import org.spongycastle.openpgp.PGPOnePassSignature;
import org.spongycastle.openpgp.PGPOnePassSignatureList; import org.spongycastle.openpgp.PGPOnePassSignatureList;
import org.spongycastle.openpgp.PGPPBEEncryptedData; import org.spongycastle.openpgp.PGPPBEEncryptedData;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; 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.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
@ -67,7 +54,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
@ -248,7 +234,7 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null; PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null; PGPPBEEncryptedData encryptedDataSymmetric = null;
PGPSecretKey secretEncryptionKey = null; WrappedSecretKey secretEncryptionKey = null;
Iterator<?> it = enc.getEncryptedDataObjects(); Iterator<?> it = enc.getEncryptedDataObjects();
boolean asymmetricPacketFound = false; boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false; boolean symmetricPacketFound = false;
@ -260,15 +246,12 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
long masterKeyId; WrappedSecretKeyRing secretKeyRing;
PGPSecretKeyRing secretKeyRing;
try { try {
// get master key id for this encryption key id
masterKeyId = mProviderHelper.getMasterKeyId(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(encData.getKeyID()))
);
// get actual keyring object based on master key id // get actual keyring object based on master key id
secretKeyRing = mProviderHelper.getPGPSecretKeyRing(masterKeyId); secretKeyRing = mProviderHelper.getWrappedSecretKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(encData.getKeyID())
);
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
// continue with the next packet in the while loop // continue with the next packet in the while loop
continue; continue;
@ -278,13 +261,14 @@ public class PgpDecryptVerify {
continue; continue;
} }
// get subkey which has been used for this encryption packet // get subkey which has been used for this encryption packet
secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID()); secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID());
if (secretEncryptionKey == null) { if (secretEncryptionKey == null) {
// continue with the next packet in the while loop // continue with the next packet in the while loop
continue; continue;
} }
/* secret key exists in database! */ /* secret key exists in database! */
long masterKeyId = secretEncryptionKey.getRing().getMasterKeyId();
// allow only specific keys for decryption? // allow only specific keys for decryption?
if (mAllowedKeyIds != null) { if (mAllowedKeyIds != null) {
@ -359,23 +343,17 @@ public class PgpDecryptVerify {
} else if (asymmetricPacketFound) { } else if (asymmetricPacketFound) {
currentProgress += 5; currentProgress += 5;
updateProgress(R.string.progress_extracting_key, currentProgress, 100); updateProgress(R.string.progress_extracting_key, currentProgress, 100);
PGPPrivateKey privateKey;
try { try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() if (!secretEncryptionKey.unlock(mPassphrase)) {
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( throw new WrongPassphraseException();
mPassphrase.toCharArray()); }
privateKey = secretEncryptionKey.extractPrivateKey(keyDecryptor); } catch(PgpGeneralException e) {
} catch (PGPException e) {
throw new WrongPassphraseException();
}
if (privateKey == null) {
throw new KeyExtractionException(); throw new KeyExtractionException();
} }
currentProgress += 5; currentProgress += 5;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100); updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder() PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory();
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory); clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
@ -388,10 +366,10 @@ public class PgpDecryptVerify {
PGPObjectFactory plainFact = new PGPObjectFactory(clear); PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject(); Object dataChunk = plainFact.nextObject();
PGPOnePassSignature signature = null;
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
PGPPublicKey signatureKey = null;
int signatureIndex = -1; int signatureIndex = -1;
WrappedPublicKeyRing signingRing = null;
WrappedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) { if (dataChunk instanceof PGPCompressedData) {
updateProgress(R.string.progress_decompressing_data, currentProgress, 100); updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
@ -403,6 +381,8 @@ public class PgpDecryptVerify {
currentProgress += 10; currentProgress += 10;
} }
PGPOnePassSignature signature = null;
if (dataChunk instanceof PGPOnePassSignatureList) { if (dataChunk instanceof PGPOnePassSignatureList) {
updateProgress(R.string.progress_processing_signature, currentProgress, 100); updateProgress(R.string.progress_processing_signature, currentProgress, 100);
@ -410,19 +390,13 @@ public class PgpDecryptVerify {
// go through all signatures // go through all signatures
// and find out for which signature we have a key in our database // and find out for which signature we have a key in our database
Long masterKeyId = null;
String primaryUserId = null;
for (int i = 0; i < sigList.size(); ++i) { for (int i = 0; i < sigList.size(); ++i) {
try { try {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri( long sigKeyId = sigList.get(i).getKeyID();
Long.toString(sigList.get(i).getKeyID())); signingRing = mProviderHelper.getWrappedPublicKeyRing(
Map<String, Object> data = mProviderHelper.getGenericData(uri, KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
ProviderHelper.FIELD_TYPE_STRING }
); );
masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID); signingKey = signingRing.getSubkey(sigKeyId);
primaryUserId = (String) data.get(KeyRings.USER_ID);
signatureIndex = i; signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!"); Log.d(Constants.TAG, "key not found!");
@ -430,43 +404,24 @@ public class PgpDecryptVerify {
} }
} }
if (masterKeyId != null) { if (signingKey != null) {
// key found in our database! // key found in our database!
signature = sigList.get(signatureIndex); signature = sigList.get(signatureIndex);
PGPPublicKeyRing publicKeyRing = null;
try {
publicKeyRing = mProviderHelper
.getPGPPublicKeyRing(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
// can't happen
}
// get the subkey which has been used to generate this signature
signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
signatureResultBuilder.signatureAvailable(true); signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true); signatureResultBuilder.knownKey(true);
signatureResultBuilder.userId(primaryUserId); signatureResultBuilder.keyId(signingRing.getMasterKeyId());
signatureResultBuilder.keyId(masterKeyId); try {
signatureResultBuilder.userId(signingRing.getPrimaryUserId());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider() new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey); signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
// get certification status of this key
boolean isSignatureKeyCertified;
try {
Object data = mProviderHelper.getGenericData(
KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
KeyRings.VERIFIED,
ProviderHelper.FIELD_TYPE_INTEGER);
isSignatureKeyCertified = ((Long) data > 0);
} catch (ProviderHelper.NotFoundException e) {
isSignatureKeyCertified = false;
}
signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
} else { } else {
// no key in our database -> return "unknown pub key" status including the first key id // no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) { if (!sigList.isEmpty()) {
@ -541,7 +496,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures // Verify signature and check binding signatures
boolean validSignature = signature.verify(messageSignature); boolean validSignature = signature.verify(messageSignature);
boolean validKeyBinding = verifyKeyBinding(messageSignature, signatureKey); boolean validKeyBinding = signingRing.verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature); signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding); signatureResultBuilder.validKeyBinding(validKeyBinding);
@ -617,22 +572,19 @@ public class PgpDecryptVerify {
throw new InvalidDataException(); throw new InvalidDataException();
} }
WrappedPublicKeyRing signingRing = null;
WrappedPublicKey signingKey = null;
int signatureIndex = -1;
// go through all signatures // go through all signatures
// and find out for which signature we have a key in our database // and find out for which signature we have a key in our database
Long masterKeyId = null;
String primaryUserId = null;
int signatureIndex = 0;
for (int i = 0; i < sigList.size(); ++i) { for (int i = 0; i < sigList.size(); ++i) {
try { try {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri( long sigKeyId = sigList.get(i).getKeyID();
Long.toString(sigList.get(i).getKeyID())); signingRing = mProviderHelper.getWrappedPublicKeyRing(
Map<String, Object> data = mProviderHelper.getGenericData(uri, KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
ProviderHelper.FIELD_TYPE_STRING }
); );
masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID); signingKey = signingRing.getSubkey(sigKeyId);
primaryUserId = (String) data.get(KeyRings.USER_ID);
signatureIndex = i; signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!"); Log.d(Constants.TAG, "key not found!");
@ -641,44 +593,25 @@ public class PgpDecryptVerify {
} }
PGPSignature signature = null; PGPSignature signature = null;
PGPPublicKey signatureKey = null;
if (masterKeyId != null) { if (signingKey != null) {
// key found in our database! // key found in our database!
signature = sigList.get(signatureIndex); signature = sigList.get(signatureIndex);
PGPPublicKeyRing publicKeyRing = null;
try {
publicKeyRing = mProviderHelper
.getPGPPublicKeyRing(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
// can't happen
}
// get the subkey which has been used to generate this signature
signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
signatureResultBuilder.signatureAvailable(true); signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true); signatureResultBuilder.knownKey(true);
signatureResultBuilder.userId(primaryUserId); signatureResultBuilder.keyId(signingRing.getMasterKeyId());
signatureResultBuilder.keyId(masterKeyId); try {
signatureResultBuilder.userId(signingRing.getPrimaryUserId());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider() new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey); signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
// get certification status of this key
boolean isSignatureKeyCertified;
try {
Object data = mProviderHelper.getGenericData(
KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
KeyRings.VERIFIED,
ProviderHelper.FIELD_TYPE_INTEGER);
isSignatureKeyCertified = ((Long) data > 0);
} catch (ProviderHelper.NotFoundException e) {
isSignatureKeyCertified = false;
}
signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
} else { } else {
// no key in our database -> return "unknown pub key" status including the first key id // no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) { if (!sigList.isEmpty()) {
@ -710,7 +643,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures // Verify signature and check binding signatures
boolean validSignature = signature.verify(); boolean validSignature = signature.verify();
boolean validKeyBinding = verifyKeyBinding(signature, signatureKey); boolean validKeyBinding = signingRing.verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature); signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding); signatureResultBuilder.validKeyBinding(validKeyBinding);
@ -722,113 +655,6 @@ public class PgpDecryptVerify {
return result; return result;
} }
private boolean verifyKeyBinding(PGPSignature signature, PGPPublicKey signatureKey) {
long signatureKeyId = signature.getKeyID();
boolean validKeyBinding = false;
PGPPublicKey mKey = null;
try {
PGPPublicKeyRing signKeyRing = mProviderHelper.getPGPPublicKeyRingWithKeyId(
signatureKeyId);
mKey = signKeyRing.getPublicKey();
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found");
}
if (signature.getKeyID() != mKey.getKeyID()) {
validKeyBinding = verifyKeyBinding(mKey, signatureKey);
} else { //if the key used to make the signature was the master key, no need to check binding sigs
validKeyBinding = true;
}
return validKeyBinding;
}
private boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
boolean validSubkeyBinding = false;
boolean validTempSubkeyBinding = false;
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
//about keys without subkey signing. Can't get it to import a slightly broken one
//either, so we will err on bad subkey binding here.
PGPSignature sig = itr.next();
if (sig.getKeyID() == masterPublicKey.getKeyID() &&
sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
//check and if ok, check primary key binding.
try {
sig.init(contentVerifierBuilderProvider, masterPublicKey);
validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey);
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
if (validTempSubkeyBinding) {
validSubkeyBinding = true;
}
if (validTempSubkeyBinding) {
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
}
}
}
return (validSubkeyBinding & validPrimaryKeyBinding);
}
private boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
PGPPublicKey masterPublicKey,
PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureList eSigList;
if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
try {
eSigList = pkts.getEmbeddedSignatures();
} catch (IOException e) {
return false;
} catch (PGPException e) {
return false;
}
for (int j = 0; j < eSigList.size(); ++j) {
PGPSignature emSig = eSigList.get(j);
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
try {
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
}
}
}
return validPrimaryKeyBinding;
}
/** /**
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle * Mostly taken from ClearSignedFileProcessor in Bouncy Castle
* *

View File

@ -24,20 +24,14 @@ import android.os.Environment;
import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException; import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -62,7 +56,6 @@ public class PgpImportExport {
private ProviderHelper mProviderHelper; private ProviderHelper mProviderHelper;
public static final int RETURN_OK = 0; public static final int RETURN_OK = 0;
public static final int RETURN_ERROR = -1;
public static final int RETURN_BAD = -2; public static final int RETURN_BAD = -2;
public static final int RETURN_UPDATED = 1; public static final int RETURN_UPDATED = 1;
@ -100,12 +93,12 @@ public class PgpImportExport {
} }
} }
public boolean uploadKeyRingToServer(HkpKeyserver server, PGPPublicKeyRing keyring) { public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null; ArmoredOutputStream aos = null;
try { try {
aos = new ArmoredOutputStream(bos); aos = new ArmoredOutputStream(bos);
aos.write(keyring.getEncoded()); keyring.encode(aos);
aos.close(); aos.close();
String armoredKey = bos.toString("UTF-8"); String armoredKey = bos.toString("UTF-8");
@ -133,7 +126,7 @@ public class PgpImportExport {
/** /**
* Imports keys from given data. If keyIds is given only those are imported * Imports keys from given data. If keyIds is given only those are imported
*/ */
public Bundle importKeyRings(List<ImportKeysListEntry> entries) public Bundle importKeyRings(List<ParcelableKeyRing> entries)
throws PgpGeneralException, PGPException, IOException { throws PgpGeneralException, PGPException, IOException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
@ -144,37 +137,26 @@ public class PgpImportExport {
int badKeys = 0; int badKeys = 0;
int position = 0; int position = 0;
try { for (ParcelableKeyRing entry : entries) {
for (ImportKeysListEntry entry : entries) { try {
Object obj = PgpConversionHelper.BytesToPGPKeyRing(entry.getBytes()); UncachedKeyRing key = entry.getUncachedKeyRing();
if (obj instanceof PGPKeyRing) { mProviderHelper.savePublicKeyRing(key);
PGPKeyRing keyring = (PGPKeyRing) obj; /*switch(status) {
case RETURN_UPDATED: oldKeys++; break;
case RETURN_OK: newKeys++; break;
case RETURN_BAD: badKeys++; break;
}*/
// TODO proper import feedback
newKeys += 1;
int status = storeKeyRingInCache(keyring); } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "Encountered bad key on import!", e);
if (status == RETURN_ERROR) { ++badKeys;
throw new PgpGeneralException(
mContext.getString(R.string.error_saving_keys));
}
// update the counts to display to the user at the end
if (status == RETURN_UPDATED) {
++oldKeys;
} else if (status == RETURN_OK) {
++newKeys;
} else if (status == RETURN_BAD) {
++badKeys;
}
} else {
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
}
position++;
updateProgress(position / entries.size() * 100, 100);
} }
} catch (Exception e) { // update progress
Log.e(Constants.TAG, "Exception on parsing key file!", e); position++;
updateProgress(position / entries.size() * 100, 100);
} }
returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys); returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys);
@ -211,9 +193,11 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100); updateProgress(progress * 100 / masterKeyIdsSize, 100);
try { try {
PGPPublicKeyRing publicKeyRing = mProviderHelper.getPGPPublicKeyRing(pubKeyMasterId); WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing(
KeychainContract.KeyRings.buildGenericKeyRingUri(pubKeyMasterId)
);
publicKeyRing.encode(arOutStream); ring.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
// TODO: inform user? // TODO: inform user?
@ -237,7 +221,8 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100); updateProgress(progress * 100 / masterKeyIdsSize, 100);
try { try {
PGPSecretKeyRing secretKeyRing = mProviderHelper.getPGPSecretKeyRing(secretKeyMasterId); WrappedSecretKeyRing secretKeyRing =
mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
secretKeyRing.encode(arOutStream); secretKeyRing.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
@ -259,53 +244,4 @@ public class PgpImportExport {
return returnData; return returnData;
} }
@SuppressWarnings("unchecked")
public int storeKeyRingInCache(PGPKeyRing keyring) {
int status = RETURN_ERROR;
try {
if (keyring instanceof PGPSecretKeyRing) {
PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring;
boolean save = true;
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
secretKeyRing.getSecretKeys())) {
if (!testSecretKey.isMasterKey()) {
if (testSecretKey.isPrivateKeyEmpty()) {
// this is bad, something is very wrong...
save = false;
status = RETURN_BAD;
}
}
}
if (save) {
// TODO: preserve certifications
// (http://osdir.com/ml/encryption.bouncy-castle.devel/2007-01/msg00054.html ?)
PGPPublicKeyRing newPubRing = null;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(
secretKeyRing.getPublicKeys())) {
if (newPubRing == null) {
newPubRing = new PGPPublicKeyRing(key.getEncoded(),
new JcaKeyFingerprintCalculator());
}
newPubRing = PGPPublicKeyRing.insertPublicKey(newPubRing, key);
}
if (newPubRing != null) {
mProviderHelper.saveKeyRing(newPubRing);
}
mProviderHelper.saveKeyRing(secretKeyRing);
status = RETURN_OK;
}
} else if (keyring instanceof PGPPublicKeyRing) {
PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
mProviderHelper.saveKeyRing(publicKeyRing);
status = RETURN_OK;
}
} catch (IOException e) {
status = RETURN_ERROR;
}
return status;
}
} }

View File

@ -52,14 +52,12 @@ public class PgpKeyHelper {
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$"); private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
@Deprecated
public static Date getCreationDate(PGPPublicKey key) { public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime(); return key.getCreationTime();
} }
public static Date getCreationDate(PGPSecretKey key) { @Deprecated
return key.getPublicKey().getCreationTime();
}
public static Date getExpiryDate(PGPPublicKey key) { public static Date getExpiryDate(PGPPublicKey key) {
Date creationDate = getCreationDate(key); Date creationDate = getCreationDate(key);
if (key.getValidDays() == 0) { if (key.getValidDays() == 0) {
@ -73,185 +71,6 @@ public class PgpKeyHelper {
return calendar.getTime(); return calendar.getTime();
} }
public static Date getExpiryDate(PGPSecretKey key) {
return getExpiryDate(key.getPublicKey());
}
public static boolean isExpired(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
Date expiryDate = getExpiryDate(key);
Date now = new Date();
if (now.compareTo(creationDate) >= 0
&& (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
return false;
}
return true;
}
@SuppressWarnings("unchecked")
public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) {
long cnt = 0;
if (keyRing == null) {
return null;
}
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (cnt == num) {
return key;
}
cnt++;
}
return null;
}
@SuppressWarnings("unchecked")
private static Vector<PGPPublicKey> getEncryptKeys(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> encryptKeys = new Vector<PGPPublicKey>();
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
if (isEncryptionKey(key)) {
encryptKeys.add(key);
}
}
return encryptKeys;
}
@SuppressWarnings("unchecked")
private static Vector<PGPSecretKey> getSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (isSigningKey(key)) {
signingKeys.add(key);
}
}
return signingKeys;
}
@SuppressWarnings("unchecked")
private static Vector<PGPSecretKey> getCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (isCertificationKey(key)) {
signingKeys.add(key);
}
}
return signingKeys;
}
private static Vector<PGPPublicKey> getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> usableKeys = new Vector<PGPPublicKey>();
Vector<PGPPublicKey> encryptKeys = getEncryptKeys(keyRing);
PGPPublicKey masterKey = null;
for (int i = 0; i < encryptKeys.size(); ++i) {
PGPPublicKey key = encryptKeys.get(i);
if (!isExpired(key) && !key.isRevoked()) {
if (key.isMasterKey()) {
masterKey = key;
} else {
usableKeys.add(key);
}
}
}
if (masterKey != null) {
usableKeys.add(masterKey);
}
return usableKeys;
}
private static Vector<PGPSecretKey> getUsableCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getCertificationKeys(keyRing);
PGPSecretKey masterKey = null;
for (int i = 0; i < signingKeys.size(); ++i) {
PGPSecretKey key = signingKeys.get(i);
if (key.isMasterKey()) {
masterKey = key;
} else {
usableKeys.add(key);
}
}
if (masterKey != null) {
usableKeys.add(masterKey);
}
return usableKeys;
}
private static Vector<PGPSecretKey> getUsableSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getSigningKeys(keyRing);
PGPSecretKey masterKey = null;
for (int i = 0; i < signingKeys.size(); ++i) {
PGPSecretKey key = signingKeys.get(i);
if (key.isMasterKey()) {
masterKey = key;
} else {
usableKeys.add(key);
}
}
if (masterKey != null) {
usableKeys.add(masterKey);
}
return usableKeys;
}
public static PGPPublicKey getFirstEncryptSubkey(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> encryptKeys = getUsableEncryptKeys(keyRing);
if (encryptKeys.size() == 0) {
Log.e(Constants.TAG, "encryptKeys is null!");
return null;
}
return encryptKeys.get(0);
}
public static PGPSecretKey getFirstCertificationSubkey(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = getUsableCertificationKeys(keyRing);
if (signingKeys.size() == 0) {
return null;
}
return signingKeys.get(0);
}
public static PGPSecretKey getFirstSigningSubkey(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = getUsableSigningKeys(keyRing);
if (signingKeys.size() == 0) {
return null;
}
return signingKeys.get(0);
}
public static int getKeyUsage(PGPSecretKey key) {
return getKeyUsage(key.getPublicKey());
}
@SuppressWarnings("unchecked")
private static int getKeyUsage(PGPPublicKey key) {
int usage = 0;
if (key.getVersion() >= 4) {
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null) {
usage |= hashed.getKeyFlags();
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null) {
usage |= unhashed.getKeyFlags();
}
}
}
return usage;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static boolean isEncryptionKey(PGPPublicKey key) { public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) { if (!key.isEncryptionKey()) {
@ -293,10 +112,6 @@ public class PgpKeyHelper {
return false; return false;
} }
public static boolean isEncryptionKey(PGPSecretKey key) {
return isEncryptionKey(key.getPublicKey());
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static boolean isSigningKey(PGPPublicKey key) { public static boolean isSigningKey(PGPPublicKey key) {
if (key.getVersion() <= 3) { if (key.getVersion() <= 3) {
@ -328,10 +143,6 @@ public class PgpKeyHelper {
return false; return false;
} }
public static boolean isSigningKey(PGPSecretKey key) {
return isSigningKey(key.getPublicKey());
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static boolean isCertificationKey(PGPPublicKey key) { public static boolean isCertificationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) { if (key.getVersion() <= 3) {
@ -358,48 +169,6 @@ public class PgpKeyHelper {
return false; return false;
} }
public static boolean isAuthenticationKey(PGPSecretKey key) {
return isAuthenticationKey(key.getPublicKey());
}
@SuppressWarnings("unchecked")
public static boolean isAuthenticationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
return true;
}
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null && (hashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
return true;
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
return true;
}
}
return false;
}
public static boolean isCertificationKey(PGPSecretKey key) {
return isCertificationKey(key.getPublicKey());
}
public static String getAlgorithmInfo(Context context, PGPPublicKey key) {
return getAlgorithmInfo(context, key.getAlgorithm(), key.getBitStrength());
}
public static String getAlgorithmInfo(Context context, PGPSecretKey key) {
return getAlgorithmInfo(context, key.getPublicKey());
}
/** /**
* TODO: Only used in HkpKeyServer. Get rid of this one! * TODO: Only used in HkpKeyServer. Get rid of this one!
*/ */

View File

@ -48,8 +48,8 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.Primes;
@ -63,6 +63,7 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
@ -124,7 +125,7 @@ public class PgpKeyOperation {
*/ */
// TODO: key flags? // TODO: key flags?
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase, public byte[] createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey) boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException, throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
PgpGeneralMsgIdException, InvalidAlgorithmParameterException { PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
@ -188,43 +189,23 @@ public class PgpKeyOperation {
PGPEncryptedData.CAST5, sha1Calc) PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), try {
sha1Calc, isMasterKey, keyEncryptor); return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, isMasterKey, keyEncryptor).getEncoded();
} catch(IOException e) {
throw new PgpGeneralMsgIdException(R.string.error_encoding);
}
} }
public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassphrase, public Pair<UncachedKeyRing,UncachedKeyRing> buildNewSecretKey(
String newPassphrase) OldSaveKeyringParcel saveParcel)
throws IOException, PGPException, NoSuchProviderException {
updateProgress(R.string.progress_building_key, 0, 100);
if (oldPassphrase == null) {
oldPassphrase = "";
}
if (newPassphrase == null) {
newPassphrase = "";
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
keyRing,
new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
.getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
return newKeyRing;
}
public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildNewSecretKey(
SaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
int usageId = saveParcel.keysUsages.get(0); int usageId = saveParcel.keysUsages.get(0);
boolean canSign; boolean canSign;
String mainUserId = saveParcel.userIds.get(0); String mainUserId = saveParcel.userIds.get(0);
PGPSecretKey masterKey = saveParcel.keys.get(0); PGPSecretKey masterKey = saveParcel.keys.get(0).getSecretKeyExternal();
// this removes all userIds and certifications previously attached to the masterPublicKey // this removes all userIds and certifications previously attached to the masterPublicKey
PGPPublicKey masterPublicKey = masterKey.getPublicKey(); PGPPublicKey masterPublicKey = masterKey.getPublicKey();
@ -299,7 +280,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) { for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 40 * (i - 1) / (saveParcel.keys.size() - 1), 100); updateProgress(40 + 40 * (i - 1) / (saveParcel.keys.size() - 1), 100);
PGPSecretKey subKey = saveParcel.keys.get(i); PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey(); PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
@ -357,17 +338,19 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(secretKeyRing, publicKeyRing); return new Pair(new UncachedKeyRing(secretKeyRing), new UncachedKeyRing(publicKeyRing));
} }
public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing mKR, public Pair<UncachedKeyRing, UncachedKeyRing> buildSecretKey(WrappedSecretKeyRing wmKR,
PGPPublicKeyRing pKR, WrappedPublicKeyRing wpKR,
SaveKeyringParcel saveParcel) OldSaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
PGPSecretKeyRing mKR = wmKR.getRing();
PGPPublicKeyRing pKR = wpKR.getRing();
updateProgress(R.string.progress_building_key, 0, 100); updateProgress(R.string.progress_building_key, 0, 100);
PGPSecretKey masterKey = saveParcel.keys.get(0);
if (saveParcel.oldPassphrase == null) { if (saveParcel.oldPassphrase == null) {
saveParcel.oldPassphrase = ""; saveParcel.oldPassphrase = "";
@ -399,12 +382,12 @@ public class PgpKeyOperation {
*/ */
if (saveParcel.deletedKeys != null) { if (saveParcel.deletedKeys != null) {
for (PGPSecretKey dKey : saveParcel.deletedKeys) { for (UncachedSecretKey dKey : saveParcel.deletedKeys) {
mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey); mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey.getSecretKeyExternal());
} }
} }
masterKey = mKR.getSecretKey(); PGPSecretKey masterKey = mKR.getSecretKey();
PGPPublicKey masterPublicKey = masterKey.getPublicKey(); PGPPublicKey masterPublicKey = masterKey.getPublicKey();
int usageId = saveParcel.keysUsages.get(0); int usageId = saveParcel.keysUsages.get(0);
@ -564,7 +547,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) { for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 50 * i / saveParcel.keys.size(), 100); updateProgress(40 + 50 * i / saveParcel.keys.size(), 100);
if (saveParcel.moddedKeys[i]) { if (saveParcel.moddedKeys[i]) {
PGPSecretKey subKey = saveParcel.keys.get(i); PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey(); PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2; PBESecretKeyDecryptor keyDecryptor2;
@ -667,7 +650,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) { for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>( for(PGPSignature sig : new IterableIterator<PGPSignature>(
secretKeyRing.getPublicKey().getSignaturesForID(uid))) { secretKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " + Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid); PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
} }
@ -678,7 +661,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) { for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>( for(PGPSignature sig : new IterableIterator<PGPSignature>(
publicKeyRing.getPublicKey().getSignaturesForID(uid))) { publicKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " + Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid); PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
} }
@ -686,10 +669,287 @@ public class PgpKeyOperation {
*/ */
return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(mKR, pKR); return new Pair<UncachedKeyRing,UncachedKeyRing>(new UncachedKeyRing(pKR),
new UncachedKeyRing(mKR));
} }
public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing sKR,
PGPPublicKeyRing pKR,
SaveKeyringParcel saveParcel,
String passphrase)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
updateProgress(R.string.progress_building_key, 0, 100);
// sort these, so we can use binarySearch later on
Arrays.sort(saveParcel.revokeSubKeys);
Arrays.sort(saveParcel.revokeUserIds);
/*
* What's gonna happen here:
*
* 1. Unlock private key
*
* 2. Create new secret key ring
*
* 3. Copy subkeys
* - Generate revocation if requested
* - Copy old cert, or generate new if change requested
*
* 4. Generate and add new subkeys
*
* 5. Copy user ids
* - Generate revocation if requested
* - Copy old cert, or generate new if primary user id status changed
*
* 6. Add new user ids
*
* 7. Generate PublicKeyRing from SecretKeyRing
*
* 8. Return pair (PublicKeyRing,SecretKeyRing)
*
*/
// 1. Unlock private key
updateProgress(R.string.progress_building_key, 0, 100);
PGPPublicKey masterPublicKey = sKR.getPublicKey();
PGPPrivateKey masterPrivateKey; {
PGPSecretKey masterKey = sKR.getSecretKey();
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
}
// 2. Create new secret key ring
updateProgress(R.string.progress_certifying_master_key, 20, 100);
// Note we do NOT use PGPKeyRingGeneraor, it's just one level too high and does stuff
// we want to do manually. Instead, we simply use a list of secret keys.
ArrayList<PGPSecretKey> secretKeys = new ArrayList<PGPSecretKey>();
ArrayList<PGPPublicKey> publicKeys = new ArrayList<PGPPublicKey>();
// 3. Copy subkeys
// - Generate revocation if requested
// - Copy old cert, or generate new if change requested
for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
PGPPublicKey pKey = sKey.getPublicKey();
if (Arrays.binarySearch(saveParcel.revokeSubKeys, sKey.getKeyID()) >= 0) {
// add revocation signature to key, if there is none yet
if (!pKey.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION).hasNext()) {
// generate revocation signature
}
}
if (saveParcel.changeSubKeys.containsKey(sKey.getKeyID())) {
// change subkey flags?
SaveKeyringParcel.SubkeyChange change = saveParcel.changeSubKeys.get(sKey.getKeyID());
// remove old subkey binding signature(s?)
for (PGPSignature sig : new IterableIterator<PGPSignature>(
pKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING))) {
pKey = PGPPublicKey.removeCertification(pKey, sig);
}
// generate and add new signature
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
sKey, pKey, change.mFlags, change.mExpiry, passphrase);
pKey = PGPPublicKey.addCertification(pKey, sig);
}
secretKeys.add(PGPSecretKey.replacePublicKey(sKey, pKey));
publicKeys.add(pKey);
}
// 4. Generate and add new subkeys
// TODO
// 5. Copy user ids
for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
// - Copy old cert, or generate new if primary user id status changed
boolean certified = false, revoked = false;
for (PGPSignature sig : new IterableIterator<PGPSignature>(
masterPublicKey.getSignaturesForID(userId))) {
// We know there are only revocation and certification types in here.
switch(sig.getSignatureType()) {
case PGPSignature.CERTIFICATION_REVOCATION:
revoked = true;
continue;
case PGPSignature.DEFAULT_CERTIFICATION:
case PGPSignature.NO_CERTIFICATION:
case PGPSignature.CASUAL_CERTIFICATION:
case PGPSignature.POSITIVE_CERTIFICATION:
// Already got one? Remove this one, then.
if (certified) {
masterPublicKey = PGPPublicKey.removeCertification(
masterPublicKey, userId, sig);
continue;
}
boolean primary = userId.equals(saveParcel.changePrimaryUserId);
// Generate a new one under certain circumstances
if (saveParcel.changePrimaryUserId != null &&
sig.getHashedSubPackets().isPrimaryUserID() != primary) {
PGPSignature cert = generateUserIdSignature(
masterPrivateKey, masterPublicKey, userId, primary);
PGPPublicKey.addCertification(masterPublicKey, userId, cert);
}
certified = true;
}
}
// - Generate revocation if requested
if (!revoked && Arrays.binarySearch(saveParcel.revokeUserIds, userId) >= 0) {
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
masterPublicKey, userId);
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
}
}
// 6. Add new user ids
for(String userId : saveParcel.addUserIds) {
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
masterPublicKey, userId, userId.equals(saveParcel.changePrimaryUserId));
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
}
// 7. Generate PublicKeyRing from SecretKeyRing
updateProgress(R.string.progress_building_master_key, 30, 100);
PGPSecretKeyRing ring = new PGPSecretKeyRing(secretKeys);
// Copy all non-self uid certificates
for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
// - Copy old cert, or generate new if primary user id status changed
boolean certified = false, revoked = false;
for (PGPSignature sig : new IterableIterator<PGPSignature>(
masterPublicKey.getSignaturesForID(userId))) {
}
}
for (PGPPublicKey newKey : publicKeys) {
PGPPublicKey oldKey = pKR.getPublicKey(newKey.getKeyID());
for (PGPSignature sig : new IterableIterator<PGPSignature>(
oldKey.getSignatures())) {
}
}
// If requested, set new passphrase
if (saveParcel.newPassphrase != null) {
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(HashAlgorithmTags.SHA1);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
// Build key encryptor based on new passphrase
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
saveParcel.newPassphrase.toCharArray());
sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
}
// 8. Return pair (PublicKeyRing,SecretKeyRing)
return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(sKR, pKR);
}
private static PGPSignature generateUserIdSignature(
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
subHashedPacketsGen.setSignatureCreationTime(false, new Date());
subHashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
subHashedPacketsGen.setPrimaryUserID(false, primary);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
}
private static PGPSignature generateRevocationSignature(
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
subHashedPacketsGen.setSignatureCreationTime(false, new Date());
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
}
private static PGPSignature generateSubkeyBindingSignature(
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
PGPSecretKey sKey, PGPPublicKey pKey,
int flags, Long expiry, String passphrase)
throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException {
// date for signing
Date todayDate = new Date();
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
// If this key can sign, we need a primary key binding signature
if ((flags & KeyFlags.SIGN_DATA) != 0) {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
passphrase.toCharArray());
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
// cross-certify signing keys
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey);
unhashedPacketsGen.setEmbeddedSignature(false, certification);
}
PGPSignatureSubpacketGenerator hashedPacketsGen;
{
hashedPacketsGen = new PGPSignatureSubpacketGenerator();
hashedPacketsGen.setSignatureCreationTime(false, todayDate);
hashedPacketsGen.setKeyFlags(false, flags);
}
if (expiry != null) {
Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
creationDate.setTime(pKey.getCreationTime());
// note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
// here we purposefully ignore partial days in each date - long type has
// no fractional part!
long numDays = (expiry / 86400000) -
(creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0) {
throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
}
hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0);
}
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
sGen.setHashedSubpackets(hashedPacketsGen.generate());
sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
return sGen.generateCertification(masterPublicKey, pKey);
}
/** /**
* Certify the given pubkeyid with the given masterkeyid. * Certify the given pubkeyid with the given masterkeyid.
* *

View File

@ -25,25 +25,14 @@ import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPLiteralData; import org.spongycastle.openpgp.PGPLiteralData;
import org.spongycastle.openpgp.PGPLiteralDataGenerator; import org.spongycastle.openpgp.PGPLiteralDataGenerator;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator; import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPV3SignatureGenerator; import org.spongycastle.openpgp.PGPV3SignatureGenerator;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -277,20 +266,17 @@ public class PgpSignEncrypt {
} }
/* Get keys for signature generation for later usage */ /* Get keys for signature generation for later usage */
PGPSecretKey signingKey = null; WrappedSecretKey signingKey = null;
PGPSecretKeyRing signingKeyRing = null;
PGPPrivateKey signaturePrivateKey = null;
String signingUserId = null;
if (enableSignature) { if (enableSignature) {
WrappedSecretKeyRing signingKeyRing;
try { try {
signingKeyRing = mProviderHelper.getPGPSecretKeyRing(mSignatureMasterKeyId); signingKeyRing = mProviderHelper.getWrappedSecretKeyRing(mSignatureMasterKeyId);
signingUserId = (String) mProviderHelper.getUnifiedData(mSignatureMasterKeyId,
KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
throw new NoSigningKeyException(); throw new NoSigningKeyException();
} }
signingKey = PgpKeyHelper.getFirstSigningSubkey(signingKeyRing); try {
if (signingKey == null) { signingKey = signingKeyRing.getSigningSubKey();
} catch(PgpGeneralException e) {
throw new NoSigningKeyException(); throw new NoSigningKeyException();
} }
@ -300,10 +286,9 @@ public class PgpSignEncrypt {
updateProgress(R.string.progress_extracting_signature_key, 0, 100); updateProgress(R.string.progress_extracting_signature_key, 0, 100);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( try {
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray()); signingKey.unlock(mSignaturePassphrase);
signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); } catch (PgpGeneralException e) {
if (signaturePrivateKey == null) {
throw new KeyExtractionException(); throw new KeyExtractionException();
} }
} }
@ -331,13 +316,12 @@ public class PgpSignEncrypt {
// Asymmetric encryption // Asymmetric encryption
for (long id : mEncryptionMasterKeyIds) { for (long id : mEncryptionMasterKeyIds) {
try { try {
PGPPublicKeyRing keyRing = mProviderHelper.getPGPPublicKeyRing(id); WrappedPublicKeyRing keyRing = mProviderHelper.getWrappedPublicKeyRing(
PGPPublicKey key = PgpKeyHelper.getFirstEncryptSubkey(keyRing); KeyRings.buildUnifiedKeyRingUri(id));
if (key != null) { WrappedPublicKey key = keyRing.getEncryptionSubKey();
JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator = cPk.addMethod(key.getPubKeyEncryptionGenerator());
new JcePublicKeyKeyEncryptionMethodGenerator(key); } catch (PgpGeneralException e) {
cPk.addMethod(pubKeyEncryptionGenerator); Log.e(Constants.TAG, "key not found!", e);
}
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
} }
@ -351,29 +335,18 @@ public class PgpSignEncrypt {
if (enableSignature) { if (enableSignature) {
updateProgress(R.string.progress_preparing_signature, 10, 100); updateProgress(R.string.progress_preparing_signature, 10, 100);
// content signer based on signing key algorithm and chosen hash algorithm try {
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
signingKey.getPublicKey().getAlgorithm(), mSignatureHashAlgorithm) if (mSignatureForceV3) {
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); signatureV3Generator = signingKey.getV3SignatureGenerator(
mSignatureHashAlgorithm,cleartext);
int signatureType; } else {
if (mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption) { signatureGenerator = signingKey.getSignatureGenerator(
// for sign-only ascii text mSignatureHashAlgorithm, cleartext);
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT; }
} else { } catch (PgpGeneralException e) {
signatureType = PGPSignature.BINARY_DOCUMENT; // TODO throw correct type of exception (which shouldn't be PGPException)
} throw new KeyExtractionException();
if (mSignatureForceV3) {
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
signatureV3Generator.init(signatureType, signaturePrivateKey);
} else {
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(signatureType, signaturePrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, signingUserId);
signatureGenerator.setHashedSubpackets(spGen.generate());
} }
} }

View File

@ -0,0 +1,171 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/** Wrapper around PGPKeyRing class, to be constructed from bytes.
*
* This class and its relatives UncachedPublicKey and UncachedSecretKey are
* used to move around pgp key rings in non crypto related (UI, mostly) code.
* It should be used for simple inspection only until it saved in the database,
* all actual crypto operations should work with WrappedKeyRings exclusively.
*
* This class is also special in that it can hold either the PGPPublicKeyRing
* or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are
* treated equally for most purposes in UI code. It is up to the programmer to
* take care of the differences.
*
* @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
* @see org.sufficientlysecure.keychain.pgp.UncachedPublicKey
* @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey
*
*/
public class UncachedKeyRing {
final PGPKeyRing mRing;
final boolean mIsSecret;
UncachedKeyRing(PGPKeyRing ring) {
mRing = ring;
mIsSecret = ring instanceof PGPSecretKeyRing;
}
public long getMasterKeyId() {
return mRing.getPublicKey().getKeyID();
}
/* TODO don't use this */
@Deprecated
public PGPKeyRing getRing() {
return mRing;
}
public UncachedPublicKey getPublicKey() {
return new UncachedPublicKey(mRing.getPublicKey());
}
public Iterator<UncachedPublicKey> getPublicKeys() {
final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
return new Iterator<UncachedPublicKey>() {
public void remove() {
it.remove();
}
public UncachedPublicKey next() {
return new UncachedPublicKey(it.next());
}
public boolean hasNext() {
return it.hasNext();
}
};
}
/** Returns the dynamic (though final) property if this is a secret keyring or not. */
public boolean isSecret() {
return mIsSecret;
}
public byte[] getEncoded() throws IOException {
return mRing.getEncoded();
}
public byte[] getFingerprint() {
return mRing.getPublicKey().getFingerprint();
}
public static UncachedKeyRing decodePublicFromData(byte[] data)
throws PgpGeneralException, IOException {
UncachedKeyRing ring = decodeFromData(data);
if(ring.isSecret()) {
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
}
return ring;
}
public static UncachedKeyRing decodeFromData(byte[] data)
throws PgpGeneralException, IOException {
BufferedInputStream bufferedInput =
new BufferedInputStream(new ByteArrayInputStream(data));
if (bufferedInput.available() > 0) {
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
// get first object in block
Object obj;
if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
return new UncachedKeyRing((PGPKeyRing) obj);
} else {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
} else {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
public static List<UncachedKeyRing> fromStream(InputStream stream)
throws PgpGeneralException, IOException {
PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
List<UncachedKeyRing> result = new Vector<UncachedKeyRing>();
// go through all objects in this block
Object obj;
while ((obj = objectFactory.nextObject()) != null) {
Log.d(Constants.TAG, "Found class: " + obj.getClass());
if (obj instanceof PGPKeyRing) {
result.add(new UncachedKeyRing((PGPKeyRing) obj));
} else {
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
}
}
return result;
}
public void encodeArmored(OutputStream out, String version) throws IOException {
ArmoredOutputStream aos = new ArmoredOutputStream(out);
aos.setHeader("Version", version);
aos.write(mRing.getEncoded());
aos.close();
}
public ArrayList<Long> getAvailableSubkeys() {
if(!isSecret()) {
throw new RuntimeException("Tried to find available subkeys from non-secret keys. " +
"This is a programming error and should never happen!");
}
ArrayList<Long> result = new ArrayList<Long>();
// then, mark exactly the keys we have available
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(
((PGPSecretKeyRing) mRing).getSecretKeys())) {
S2K s2k = sub.getS2K();
// Set to 1, except if the encryption type is GNU_DUMMY_S2K
if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
result.add(sub.getKeyID());
}
}
return result;
}
}

View File

@ -0,0 +1,197 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.IterableIterator;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
public class UncachedPublicKey {
protected final PGPPublicKey mPublicKey;
private Integer mCacheUsage = null;
public UncachedPublicKey(PGPPublicKey key) {
mPublicKey = key;
}
public long getKeyId() {
return mPublicKey.getKeyID();
}
/** The revocation signature is NOT checked here, so this may be false! */
public boolean maybeRevoked() {
return mPublicKey.isRevoked();
}
public Date getCreationTime() {
return mPublicKey.getCreationTime();
}
public Date getExpiryTime() {
Date creationDate = getCreationTime();
if (mPublicKey.getValidDays() == 0) {
// no expiry
return null;
}
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(creationDate);
calendar.add(Calendar.DATE, mPublicKey.getValidDays());
return calendar.getTime();
}
public boolean isExpired() {
Date creationDate = mPublicKey.getCreationTime();
Date expiryDate = mPublicKey.getValidSeconds() > 0
? new Date(creationDate.getTime() + mPublicKey.getValidSeconds() * 1000) : null;
Date now = new Date();
return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
}
public boolean isMasterKey() {
return mPublicKey.isMasterKey();
}
public int getAlgorithm() {
return mPublicKey.getAlgorithm();
}
public int getBitStrength() {
return mPublicKey.getBitStrength();
}
public String getPrimaryUserId() {
for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignaturesForID(userId))) {
if (sig.getHashedSubPackets() != null
&& sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
try {
// make sure it's actually valid
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
if (sig.verifyCertification(userId, mPublicKey)) {
return userId;
}
} catch (Exception e) {
// nothing bad happens, the key is just not considered the primary key id
}
}
}
}
return null;
}
public ArrayList<String> getUnorderedUserIds() {
ArrayList<String> userIds = new ArrayList<String>();
for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
userIds.add(userId);
}
return userIds;
}
public boolean isElGamalEncrypt() {
return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
}
public boolean isDSA() {
return getAlgorithm() == PGPPublicKey.DSA;
}
@SuppressWarnings("unchecked")
public int getKeyUsage() {
if(mCacheUsage == null) {
mCacheUsage = 0;
if (mPublicKey.getVersion() >= 4) {
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignatures())) {
if (mPublicKey.isMasterKey() && sig.getKeyID() != mPublicKey.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null) {
mCacheUsage |= hashed.getKeyFlags();
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null) {
mCacheUsage |= unhashed.getKeyFlags();
}
}
}
}
return mCacheUsage;
}
public boolean canAuthenticate() {
return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.AUTHENTICATION) != 0;
}
public boolean canCertify() {
return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.CERTIFY_OTHER) != 0;
}
public boolean canEncrypt() {
if (!mPublicKey.isEncryptionKey()) {
return false;
}
// special cases
if (mPublicKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
return true;
}
if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
return true;
}
return mPublicKey.getVersion() <= 3 ||
(getKeyUsage() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0;
}
public boolean canSign() {
// special case
if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
return true;
}
return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.SIGN_DATA) != 0;
}
public byte[] getFingerprint() {
return mPublicKey.getFingerprint();
}
// TODO This method should have package visibility - no access outside the pgp package!
// (It's still used in ProviderHelper at this point)
public PGPPublicKey getPublicKey() {
return mPublicKey;
}
public Iterator<WrappedSignature> getSignaturesForId(String userId) {
final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);
return new Iterator<WrappedSignature>() {
public void remove() {
it.remove();
}
public WrappedSignature next() {
return new WrappedSignature(it.next());
}
public boolean hasNext() {
return it.hasNext();
}
};
}
}

View File

@ -0,0 +1,33 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPSecretKey;
import java.io.IOException;
import java.io.OutputStream;
public class UncachedSecretKey extends UncachedPublicKey {
public static final int CERTIFY_OTHER = KeyFlags.CERTIFY_OTHER;
public static final int SIGN_DATA = KeyFlags.SIGN_DATA;
public static final int ENCRYPT_COMMS = KeyFlags.ENCRYPT_COMMS;
public static final int ENCRYPT_STORAGE = KeyFlags.ENCRYPT_STORAGE;
public static final int AUTHENTICATION = KeyFlags.AUTHENTICATION;
final PGPSecretKey mSecretKey;
public UncachedSecretKey(PGPSecretKey secretKey) {
super(secretKey.getPublicKey());
mSecretKey = secretKey;
}
@Deprecated
public PGPSecretKey getSecretKeyExternal() {
return mSecretKey;
}
public void encodeSecretKey(OutputStream os) throws IOException {
mSecretKey.encode(os);
}
}

View File

@ -0,0 +1,97 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import java.io.IOException;
import java.io.OutputStream;
/** A generic wrapped PGPKeyRing object.
*
* This class provides implementations for all basic getters which both
* PublicKeyRing and SecretKeyRing have in common. To make the wrapped keyring
* class typesafe in implementing subclasses, the field is stored in the
* implementing class, providing properly typed access through the getRing
* getter method.
*
*/
public abstract class WrappedKeyRing extends KeyRing {
private final boolean mHasAnySecret;
private final int mVerified;
WrappedKeyRing(boolean hasAnySecret, int verified) {
mHasAnySecret = hasAnySecret;
mVerified = verified;
}
public long getMasterKeyId() {
return getRing().getPublicKey().getKeyID();
}
public boolean hasAnySecret() {
return mHasAnySecret;
}
public int getVerified() {
return mVerified;
}
public String getPrimaryUserId() throws PgpGeneralException {
return (String) getRing().getPublicKey().getUserIDs().next();
};
public boolean isRevoked() throws PgpGeneralException {
// Is the master key revoked?
return getRing().getPublicKey().isRevoked();
}
public boolean canCertify() throws PgpGeneralException {
return getRing().getPublicKey().isEncryptionKey();
}
public long getEncryptId() throws PgpGeneralException {
for(PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
if(PgpKeyHelper.isEncryptionKey(key)) {
return key.getKeyID();
}
}
throw new PgpGeneralException("No valid encryption key found!");
}
public boolean hasEncrypt() throws PgpGeneralException {
try {
getEncryptId();
return true;
} catch(PgpGeneralException e) {
return false;
}
}
public long getSignId() throws PgpGeneralException {
for(PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
if(PgpKeyHelper.isSigningKey(key)) {
return key.getKeyID();
}
}
throw new PgpGeneralException("No valid signing key found!");
}
public boolean hasSign() throws PgpGeneralException {
try {
getSignId();
return true;
} catch (PgpGeneralException e) {
return false;
}
}
public void encode(OutputStream stream) throws IOException {
getRing().encode(stream);
}
abstract PGPKeyRing getRing();
}

View File

@ -0,0 +1,39 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.util.IterableIterator;
/** Wrapper for a PGPPublicKey.
*
* The methods implemented in this class are a thin layer over
* UncachedPublicKey. The difference between the two classes is that objects of
* this class can only be obtained from a WrappedKeyRing, and that it stores a
* back reference to its parent as well. A method which works with
* WrappedPublicKey is therefore guaranteed to work on a KeyRing which is
* stored in the database.
*
*/
public class WrappedPublicKey extends UncachedPublicKey {
// this is the parent key ring
final KeyRing mRing;
WrappedPublicKey(KeyRing ring, PGPPublicKey key) {
super(key);
mRing = ring;
}
public IterableIterator<String> getUserIds() {
return new IterableIterator<String>(mPublicKey.getUserIDs());
}
public KeyRing getKeyRing() {
return mRing;
}
JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() {
return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey);
}
}

View File

@ -0,0 +1,192 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.Iterator;
public class WrappedPublicKeyRing extends WrappedKeyRing {
private PGPPublicKeyRing mRing;
private final byte[] mPubKey;
public WrappedPublicKeyRing(byte[] blob, boolean hasAnySecret, int verified) {
super(hasAnySecret, verified);
mPubKey = blob;
}
PGPPublicKeyRing getRing() {
if(mRing == null) {
PGPObjectFactory factory = new PGPObjectFactory(mPubKey);
PGPKeyRing keyRing = null;
try {
if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
Log.e(Constants.TAG, "No keys given!");
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
}
mRing = (PGPPublicKeyRing) keyRing;
}
return mRing;
}
public void encode(ArmoredOutputStream stream) throws IOException {
getRing().encode(stream);
}
public WrappedPublicKey getSubkey() {
return new WrappedPublicKey(this, getRing().getPublicKey());
}
public WrappedPublicKey getSubkey(long id) {
return new WrappedPublicKey(this, getRing().getPublicKey(id));
}
/** Getter that returns the subkey that should be used for signing. */
WrappedPublicKey getEncryptionSubKey() throws PgpGeneralException {
PGPPublicKey key = getRing().getPublicKey(getEncryptId());
if(key != null) {
WrappedPublicKey cKey = new WrappedPublicKey(this, key);
if(!cKey.canEncrypt()) {
throw new PgpGeneralException("key error");
}
return cKey;
}
// TODO handle with proper exception
throw new PgpGeneralException("no encryption key available");
}
public boolean verifySubkeyBinding(WrappedPublicKey cachedSubkey) {
boolean validSubkeyBinding = false;
boolean validTempSubkeyBinding = false;
boolean validPrimaryKeyBinding = false;
PGPPublicKey masterKey = getRing().getPublicKey();
PGPPublicKey subKey = cachedSubkey.getPublicKey();
// Is this the master key? Match automatically, then.
if(Arrays.equals(masterKey.getFingerprint(), subKey.getFingerprint())) {
return true;
}
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
Iterator<PGPSignature> itr = subKey.getSignatures();
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
//about keys without subkey signing. Can't get it to import a slightly broken one
//either, so we will err on bad subkey binding here.
PGPSignature sig = itr.next();
if (sig.getKeyID() == masterKey.getKeyID() &&
sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
//check and if ok, check primary key binding.
try {
sig.init(contentVerifierBuilderProvider, masterKey);
validTempSubkeyBinding = sig.verifyCertification(masterKey, subKey);
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
if (validTempSubkeyBinding) {
validSubkeyBinding = true;
}
if (validTempSubkeyBinding) {
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
masterKey, subKey);
if (validPrimaryKeyBinding) {
break;
}
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
masterKey, subKey);
if (validPrimaryKeyBinding) {
break;
}
}
}
}
return validSubkeyBinding && validPrimaryKeyBinding;
}
static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
PGPPublicKey masterPublicKey,
PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureList eSigList;
if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
try {
eSigList = pkts.getEmbeddedSignatures();
} catch (IOException e) {
return false;
} catch (PGPException e) {
return false;
}
for (int j = 0; j < eSigList.size(); ++j) {
PGPSignature emSig = eSigList.get(j);
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
try {
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
}
}
}
return validPrimaryKeyBinding;
}
public IterableIterator<WrappedPublicKey> iterator() {
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public WrappedPublicKey next() {
return new WrappedPublicKey(WrappedPublicKeyRing.this, it.next());
}
@Override
public void remove() {
it.remove();
}
});
}
}

View File

@ -0,0 +1,200 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.util.List;
/** Wrapper for a PGPSecretKey.
*
* This object can only be obtained from a WrappedSecretKeyRing, and stores a
* back reference to its parent.
*
* This class represents known secret keys which are stored in the database.
* All "crypto operations using a known secret key" should be implemented in
* this class, to ensure on type level that these operations are performed on
* properly imported secret keys only.
*
*/
public class WrappedSecretKey extends WrappedPublicKey {
private final PGPSecretKey mSecretKey;
private PGPPrivateKey mPrivateKey = null;
WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) {
super(ring, key.getPublicKey());
mSecretKey = key;
}
public WrappedSecretKeyRing getRing() {
return (WrappedSecretKeyRing) mRing;
}
/** Returns the wrapped PGPSecretKeyRing.
* This function is for compatibility only, should not be used anymore and will be removed
*/
@Deprecated
public PGPSecretKey getKeyExternal() {
return mSecretKey;
}
public boolean unlock(String passphrase) throws PgpGeneralException {
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
return false;
}
if(mPrivateKey == null) {
throw new PgpGeneralException("error extracting key");
}
return true;
}
public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext)
throws PgpGeneralException {
if(mPrivateKey == null) {
throw new PrivateKeyNotUnlockedException();
}
// content signer based on signing key algorithm and chosen hash algorithm
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
mSecretKey.getPublicKey().getAlgorithm(), hashAlgo)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
int signatureType;
if (cleartext) {
// for sign-only ascii text
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
} else {
signatureType = PGPSignature.BINARY_DOCUMENT;
}
try {
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(signatureType, mPrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, mRing.getPrimaryUserId());
signatureGenerator.setHashedSubpackets(spGen.generate());
return signatureGenerator;
} catch(PGPException e) {
throw new PgpGeneralException("Error initializing signature!", e);
}
}
public PGPV3SignatureGenerator getV3SignatureGenerator(int hashAlgo, boolean cleartext)
throws PgpGeneralException {
if(mPrivateKey == null) {
throw new PrivateKeyNotUnlockedException();
}
// content signer based on signing key algorithm and chosen hash algorithm
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
mSecretKey.getPublicKey().getAlgorithm(), hashAlgo)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
int signatureType;
if (cleartext) {
// for sign-only ascii text
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
} else {
signatureType = PGPSignature.BINARY_DOCUMENT;
}
try {
PGPV3SignatureGenerator signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
signatureV3Generator.init(signatureType, mPrivateKey);
return signatureV3Generator;
} catch(PGPException e) {
throw new PgpGeneralException("Error initializing signature!", e);
}
}
public PublicKeyDataDecryptorFactory getDecryptorFactory() {
if(mPrivateKey == null) {
throw new PrivateKeyNotUnlockedException();
}
return new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
}
/**
* Certify the given pubkeyid with the given masterkeyid.
*
* @param publicKeyRing Keyring to add certification to.
* @param userIds User IDs to certify, must not be null or empty
* @return A keyring with added certifications
*/
public UncachedKeyRing certifyUserIds(WrappedPublicKeyRing publicKeyRing, List<String> userIds)
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
if(mPrivateKey == null) {
throw new PrivateKeyNotUnlockedException();
}
// create a signatureGenerator from the supplied masterKeyId and passphrase
PGPSignatureGenerator signatureGenerator;
{
// TODO: SHA256 fixed?
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
mSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
}
{ // supply signatureGenerator with a SubpacketVector
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
PGPSignatureSubpacketVector packetVector = spGen.generate();
signatureGenerator.setHashedSubpackets(packetVector);
}
// get the master subkey (which we certify for)
PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey();
// fetch public key ring, add the certification and return it
for (String userId : new IterableIterator<String>(userIds.iterator())) {
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
}
PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
return new UncachedKeyRing(ring);
}
static class PrivateKeyNotUnlockedException extends RuntimeException {
// this exception is a programming error which happens when an operation which requires
// the private key is called without a previous call to unlock()
}
public UncachedSecretKey getUncached() {
return new UncachedSecretKey(mSecretKey);
}
}

View File

@ -0,0 +1,141 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.NoSuchProviderException;
import java.util.Iterator;
public class WrappedSecretKeyRing extends WrappedKeyRing {
private PGPSecretKeyRing mRing;
public WrappedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
{
super(isRevoked, verified);
PGPObjectFactory factory = new PGPObjectFactory(blob);
PGPKeyRing keyRing = null;
try {
if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
Log.e(Constants.TAG, "No keys given!");
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
}
mRing = (PGPSecretKeyRing) keyRing;
}
PGPSecretKeyRing getRing() {
return mRing;
}
public WrappedSecretKey getSubKey() {
return new WrappedSecretKey(this, mRing.getSecretKey());
}
public WrappedSecretKey getSubKey(long id) {
return new WrappedSecretKey(this, mRing.getSecretKey(id));
}
/** Getter that returns the subkey that should be used for signing. */
WrappedSecretKey getSigningSubKey() throws PgpGeneralException {
PGPSecretKey key = mRing.getSecretKey(getSignId());
if(key != null) {
WrappedSecretKey cKey = new WrappedSecretKey(this, key);
if(!cKey.canSign()) {
throw new PgpGeneralException("key error");
}
return cKey;
}
// TODO handle with proper exception
throw new PgpGeneralException("no signing key available");
}
public boolean hasPassphrase() {
PGPSecretKey secretKey = null;
boolean foundValidKey = false;
for (Iterator keys = mRing.getSecretKeys(); keys.hasNext(); ) {
secretKey = (PGPSecretKey) keys.next();
if (!secretKey.isPrivateKeyEmpty()) {
foundValidKey = true;
break;
}
}
if(!foundValidKey) {
return false;
}
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider("SC").build("".toCharArray());
PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
return testKey == null;
} catch(PGPException e) {
// this means the crc check failed -> passphrase required
return true;
}
}
public UncachedKeyRing changeSecretKeyPassphrase(String oldPassphrase,
String newPassphrase)
throws IOException, PGPException, NoSuchProviderException {
if (oldPassphrase == null) {
oldPassphrase = "";
}
if (newPassphrase == null) {
newPassphrase = "";
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
mRing,
new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
new JcePBESecretKeyEncryptorBuilder(mRing.getSecretKey()
.getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
return new UncachedKeyRing(newKeyRing);
}
public IterableIterator<WrappedSecretKey> iterator() {
final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public WrappedSecretKey next() {
return new WrappedSecretKey(WrappedSecretKeyRing.this, it.next());
}
@Override
public void remove() {
it.remove();
}
});
}
public UncachedKeyRing getUncached() {
return new UncachedKeyRing(mRing);
}
}

View File

@ -0,0 +1,161 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.SignatureSubpacket;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.RevocationReason;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
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.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.SignatureException;
import java.util.Date;
/** OpenKeychain wrapper around PGPSignature objects.
*
* This is a mostly simple wrapper around a single bouncycastle PGPSignature
* object. It exposes high level getters for all relevant information, methods
* for verification of various signatures (uid binding, subkey binding, generic
* bytes), and a static method for construction from bytes.
*
*/
public class WrappedSignature {
public static final int DEFAULT_CERTIFICATION = PGPSignature.DEFAULT_CERTIFICATION;
public static final int NO_CERTIFICATION = PGPSignature.NO_CERTIFICATION;
public static final int CASUAL_CERTIFICATION = PGPSignature.CASUAL_CERTIFICATION;
public static final int POSITIVE_CERTIFICATION = PGPSignature.POSITIVE_CERTIFICATION;
public static final int CERTIFICATION_REVOCATION = PGPSignature.CERTIFICATION_REVOCATION;
final PGPSignature mSig;
protected WrappedSignature(PGPSignature sig) {
mSig = sig;
}
public long getKeyId() {
return mSig.getKeyID();
}
public int getSignatureType() {
return mSig.getSignatureType();
}
public int getKeyAlgorithm() {
return mSig.getKeyAlgorithm();
}
public Date getCreationTime() {
return mSig.getCreationTime();
}
public byte[] getEncoded() throws IOException {
return mSig.getEncoded();
}
public boolean isRevocation() {
return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
}
public boolean isPrimaryUserId() {
return mSig.getHashedSubPackets().isPrimaryUserID();
}
public String getRevocationReason() throws PgpGeneralException {
if(!isRevocation()) {
throw new PgpGeneralException("Not a revocation signature.");
}
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(
SignatureSubpacketTags.REVOCATION_REASON);
// For some reason, this is missing in SignatureSubpacketInputStream:146
if (!(p instanceof RevocationReason)) {
p = new RevocationReason(false, p.getData());
}
return ((RevocationReason) p).getRevocationDescription();
}
public void init(WrappedPublicKey key) throws PgpGeneralException {
init(key.getPublicKey());
}
public void init(UncachedPublicKey key) throws PgpGeneralException {
init(key.getPublicKey());
}
protected void init(PGPPublicKey key) throws PgpGeneralException {
try {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
mSig.init(contentVerifierBuilderProvider, key);
} catch(PGPException e) {
throw new PgpGeneralException(e);
}
}
public void update(byte[] data, int offset, int length) throws PgpGeneralException {
try {
mSig.update(data, offset, length);
} catch(SignatureException e) {
throw new PgpGeneralException(e);
}
}
public void update(byte data) throws PgpGeneralException {
try {
mSig.update(data);
} catch(SignatureException e) {
throw new PgpGeneralException(e);
}
}
public boolean verify() throws PgpGeneralException {
try {
return mSig.verify();
} catch(SignatureException e) {
throw new PgpGeneralException(e);
} catch(PGPException e) {
throw new PgpGeneralException(e);
}
}
protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
try {
return mSig.verifyCertification(uid, key);
} catch (SignatureException e) {
throw new PgpGeneralException("Error!", e);
} catch (PGPException e) {
throw new PgpGeneralException("Error!", e);
}
}
public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
public static WrappedSignature fromBytes(byte[] data) {
PGPObjectFactory factory = new PGPObjectFactory(data);
PGPSignatureList signatures = null;
try {
if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) {
Log.e(Constants.TAG, "No signatures given!");
return null;
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPSignature!", e);
return null;
}
return new WrappedSignature(signatures.get(0));
}
}

View File

@ -27,4 +27,7 @@ public class PgpGeneralException extends Exception {
public PgpGeneralException(String message, Throwable cause) { public PgpGeneralException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public PgpGeneralException(Throwable cause) {
super(cause);
}
} }

View File

@ -0,0 +1,161 @@
package org.sufficientlysecure.keychain.provider;
import android.net.Uri;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.Log;
/** This implementation of KeyRing provides a cached view of PublicKeyRing
* objects based on database queries exclusively.
*
* This class should be used where only few points of data but no actual
* cryptographic operations are required about a PublicKeyRing which is already
* in the database. This happens commonly in UI code, where parsing of a PGP
* key for examination would be a very expensive operation.
*
* Each getter method is implemented using a more or less expensive database
* query, while object construction is (almost) free. A common pattern is
* mProviderHelper.getCachedKeyRing(uri).getterMethod()
*
* TODO Ensure that the values returned here always match the ones returned by
* the parsed KeyRing!
*
*/
public class CachedPublicKeyRing extends KeyRing {
final ProviderHelper mProviderHelper;
final Uri mUri;
public CachedPublicKeyRing(ProviderHelper providerHelper, Uri uri) {
mProviderHelper = providerHelper;
mUri = uri;
}
public long getMasterKeyId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID, ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data;
} catch (ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
/**
* Find the master key id related to a given query. The id will either be extracted from the
* query, which should work for all specific /key_rings/ queries, or will be queried if it can't.
*/
public long extractOrGetMasterKeyId() throws PgpGeneralException {
// try extracting from the uri first
String firstSegment = mUri.getPathSegments().get(1);
if (!firstSegment.equals("find")) try {
return Long.parseLong(firstSegment);
} catch (NumberFormatException e) {
// didn't work? oh well.
Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
}
return getMasterKeyId();
}
public String getPrimaryUserId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_STRING);
return (String) data;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean isRevoked() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean canCertify() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public long getEncryptId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean hasEncrypt() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public long getSignId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean hasSign() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public int getVerified() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Integer) data;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean hasAnySecret() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
}

View File

@ -110,6 +110,8 @@ public class KeychainContract {
public static final String HAS_ANY_SECRET = "has_any_secret"; public static final String HAS_ANY_SECRET = "has_any_secret";
public static final String HAS_ENCRYPT = "has_encrypt"; public static final String HAS_ENCRYPT = "has_encrypt";
public static final String HAS_SIGN = "has_sign"; public static final String HAS_SIGN = "has_sign";
public static final String PUBKEY_DATA = "pubkey_data";
public static final String PRIVKEY_DATA = "privkey_data";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build(); .appendPath(BASE_KEY_RINGS).build();
@ -123,6 +125,10 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build(); return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
} }
public static Uri buildGenericKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build();
}
public static Uri buildGenericKeyRingUri(String masterKeyId) { public static Uri buildGenericKeyRingUri(String masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(masterKeyId).build(); return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
} }
@ -131,20 +137,24 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).build(); return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).build();
} }
public static Uri buildUnifiedKeyRingUri(String masterKeyId) { public static Uri buildUnifiedKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_UNIFIED).build(); return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId))
.appendPath(PATH_UNIFIED).build();
} }
public static Uri buildUnifiedKeyRingUri(Uri uri) { public static Uri buildUnifiedKeyRingUri(Uri uri) {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_UNIFIED).build(); return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1))
.appendPath(PATH_UNIFIED).build();
} }
public static Uri buildUnifiedKeyRingsFindByEmailUri(String email) { public static Uri buildUnifiedKeyRingsFindByEmailUri(String email) {
return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_EMAIL).appendPath(email).build(); return CONTENT_URI.buildUpon().appendPath(PATH_FIND)
.appendPath(PATH_BY_EMAIL).appendPath(email).build();
} }
public static Uri buildUnifiedKeyRingsFindBySubkeyUri(String subkey) { public static Uri buildUnifiedKeyRingsFindBySubkeyUri(long subkey) {
return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_SUBKEY).appendPath(subkey).build(); return CONTENT_URI.buildUpon().appendPath(PATH_FIND)
.appendPath(PATH_BY_SUBKEY).appendPath(Long.toString(subkey)).build();
} }
} }

View File

@ -23,11 +23,9 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns; import android.provider.BaseColumns;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
@ -256,6 +254,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
}.getReadableDatabase(); }.getReadableDatabase();
Cursor cursor = null; Cursor cursor = null;
ProviderHelper providerHelper = new ProviderHelper(context);
try { try {
// we insert in two steps: first, all public keys that have secret keys // we insert in two steps: first, all public keys that have secret keys
cursor = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS (" cursor = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
@ -266,14 +266,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
for (int i = 0; i < cursor.getCount(); i++) { for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i); cursor.moveToPosition(i);
byte[] data = cursor.getBlob(0); byte[] data = cursor.getBlob(0);
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data); try {
ProviderHelper providerHelper = new ProviderHelper(context); UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
if (ring instanceof PGPPublicKeyRing) providerHelper.savePublicKeyRing(ring);
providerHelper.saveKeyRing((PGPPublicKeyRing) ring); } catch(PgpGeneralException e) {
else if (ring instanceof PGPSecretKeyRing) Log.e(Constants.TAG, "Error decoding keyring blob!");
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
else {
Log.e(Constants.TAG, "Unknown blob data type!");
} }
} }
} }
@ -293,14 +290,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
for (int i = 0; i < cursor.getCount(); i++) { for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i); cursor.moveToPosition(i);
byte[] data = cursor.getBlob(0); byte[] data = cursor.getBlob(0);
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data); try {
ProviderHelper providerHelper = new ProviderHelper(context); UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
if (ring instanceof PGPPublicKeyRing) { providerHelper.savePublicKeyRing(ring);
providerHelper.saveKeyRing((PGPPublicKeyRing) ring); } catch(PgpGeneralException e) {
} else if (ring instanceof PGPSecretKeyRing) { Log.e(Constants.TAG, "Error decoding keyring blob!");
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
} else {
Log.e(Constants.TAG, "Unknown blob data type!");
} }
} }
} }

View File

@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
public class KeychainProvider extends ContentProvider { public class KeychainProvider extends ContentProvider {
@ -242,45 +243,39 @@ public class KeychainProvider extends ContentProvider {
HashMap<String, String> projectionMap = new HashMap<String, String>(); HashMap<String, String> projectionMap = new HashMap<String, String>();
projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id"); projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID); projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
projectionMap.put(KeyRings.KEY_ID, Keys.KEY_ID); projectionMap.put(KeyRings.KEY_ID, Tables.KEYS + "." + Keys.KEY_ID);
projectionMap.put(KeyRings.KEY_SIZE, Keys.KEY_SIZE); projectionMap.put(KeyRings.KEY_SIZE, Tables.KEYS + "." + Keys.KEY_SIZE);
projectionMap.put(KeyRings.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED); projectionMap.put(KeyRings.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED);
projectionMap.put(KeyRings.CAN_CERTIFY, Keys.CAN_CERTIFY); projectionMap.put(KeyRings.CAN_CERTIFY, Tables.KEYS + "." + Keys.CAN_CERTIFY);
projectionMap.put(KeyRings.CAN_ENCRYPT, Keys.CAN_ENCRYPT); projectionMap.put(KeyRings.CAN_ENCRYPT, Tables.KEYS + "." + Keys.CAN_ENCRYPT);
projectionMap.put(KeyRings.CAN_SIGN, Keys.CAN_SIGN); projectionMap.put(KeyRings.CAN_SIGN, Tables.KEYS + "." + Keys.CAN_SIGN);
projectionMap.put(KeyRings.CREATION, Tables.KEYS + "." + Keys.CREATION); projectionMap.put(KeyRings.CREATION, Tables.KEYS + "." + Keys.CREATION);
projectionMap.put(KeyRings.EXPIRY, Keys.EXPIRY); projectionMap.put(KeyRings.EXPIRY, Tables.KEYS + "." + Keys.EXPIRY);
projectionMap.put(KeyRings.ALGORITHM, Keys.ALGORITHM); projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM);
projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT); projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT);
projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID); projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED); projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED);
projectionMap.put(KeyRings.HAS_SECRET, KeyRings.HAS_SECRET); projectionMap.put(KeyRings.PUBKEY_DATA,
Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA
+ " AS " + KeyRings.PUBKEY_DATA);
projectionMap.put(KeyRings.PRIVKEY_DATA,
Tables.KEY_RINGS_SECRET + "." + KeyRingData.KEY_RING_DATA
+ " AS " + KeyRings.PRIVKEY_DATA);
projectionMap.put(KeyRings.HAS_SECRET, Tables.KEYS + "." + KeyRings.HAS_SECRET);
projectionMap.put(KeyRings.HAS_ANY_SECRET, projectionMap.put(KeyRings.HAS_ANY_SECRET,
"(EXISTS (SELECT * FROM " + Tables.KEY_RINGS_SECRET "(EXISTS (SELECT * FROM " + Tables.KEY_RINGS_SECRET
+ " WHERE " + Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID + " WHERE " + Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ ")) AS " + KeyRings.HAS_ANY_SECRET); + ")) AS " + KeyRings.HAS_ANY_SECRET);
projectionMap.put(KeyRings.HAS_ENCRYPT, projectionMap.put(KeyRings.HAS_ENCRYPT,
"(EXISTS (SELECT * FROM " + Tables.KEYS + " AS k" "kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT);
+" WHERE k." + Keys.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND k." + Keys.IS_REVOKED + " = 0"
+ " AND k." + Keys.CAN_ENCRYPT + " = 1"
+ " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY
+ " >= " + new Date().getTime() / 1000 + " )"
+ ")) AS " + KeyRings.HAS_ENCRYPT);
projectionMap.put(KeyRings.HAS_SIGN, projectionMap.put(KeyRings.HAS_SIGN,
"(EXISTS (SELECT * FROM " + Tables.KEYS + " AS k" "kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN);
+" WHERE k." + Keys.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND k." + Keys.IS_REVOKED + " = 0"
+ " AND k." + Keys.HAS_SECRET + " = 1"
+ " AND k." + Keys.CAN_SIGN + " = 1"
+ " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY
+ " >= " + new Date().getTime() / 1000 + " )"
+ ")) AS " + KeyRings.HAS_SIGN);
qb.setProjectionMap(projectionMap); qb.setProjectionMap(projectionMap);
// Need this as list so we can search in it
List<String> plist = Arrays.asList(projection);
qb.setTables( qb.setTables(
Tables.KEYS Tables.KEYS
+ " INNER JOIN " + Tables.USER_IDS + " ON (" + " INNER JOIN " + Tables.USER_IDS + " ON ("
@ -295,6 +290,37 @@ public class KeychainProvider extends ContentProvider {
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED + " AND " + Tables.CERTS + "." + Certs.VERIFIED
+ " = " + Certs.VERIFIED_SECRET + " = " + Certs.VERIFIED_SECRET
+ ")" + ")"
// fairly expensive joins following, only do when requested
+ (plist.contains(KeyRings.PUBKEY_DATA) ?
" INNER JOIN " + Tables.KEY_RINGS_PUBLIC + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
+ Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.MASTER_KEY_ID
+ ")" : "")
+ (plist.contains(KeyRings.PRIVKEY_DATA) ?
" LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
+ Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ ")" : "")
+ (plist.contains(KeyRings.HAS_ENCRYPT) ?
" LEFT JOIN " + Tables.KEYS + " AS kE ON ("
+"kE." + Keys.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND kE." + Keys.IS_REVOKED + " = 0"
+ " AND kE." + Keys.CAN_ENCRYPT + " = 1"
+ " AND ( kE." + Keys.EXPIRY + " IS NULL OR kE." + Keys.EXPIRY
+ " >= " + new Date().getTime() / 1000 + " )"
+ ")" : "")
+ (plist.contains(KeyRings.HAS_SIGN) ?
" LEFT JOIN " + Tables.KEYS + " AS kS ON ("
+"kS." + Keys.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND kS." + Keys.IS_REVOKED + " = 0"
+ " AND kS." + Keys.CAN_SIGN + " = 1"
+ " AND ( kS." + Keys.EXPIRY + " IS NULL OR kS." + Keys.EXPIRY
+ " >= " + new Date().getTime() / 1000 + " )"
+ ")" : "")
); );
qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0"); qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
// in case there are multiple verifying certificates // in case there are multiple verifying certificates
@ -595,7 +621,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RING_CERTS: case KEY_RING_CERTS:
// we replace here, keeping only the latest signature // we replace here, keeping only the latest signature
// TODO this would be better handled in saveKeyRing directly! // TODO this would be better handled in savePublicKeyRing directly!
db.replaceOrThrow(Tables.CERTS, null, values); db.replaceOrThrow(Tables.CERTS, null, values);
keyId = values.getAsLong(Certs.MASTER_KEY_ID); keyId = values.getAsLong(Certs.MASTER_KEY_ID);
break; break;
@ -618,7 +644,7 @@ public class KeychainProvider extends ContentProvider {
} }
if(keyId != null) { if(keyId != null) {
uri = KeyRings.buildGenericKeyRingUri(keyId.toString()); uri = KeyRings.buildGenericKeyRingUri(keyId);
rowUri = uri; rowUri = uri;
} }

View File

@ -23,26 +23,20 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.OperationApplicationException; import android.content.OperationApplicationException;
import android.database.Cursor; import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri; import android.net.Uri;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v4.util.LongSparseArray; import android.support.v4.util.LongSparseArray;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
@ -56,14 +50,12 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.security.SignatureException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
public class ProviderHelper { public class ProviderHelper {
@ -141,47 +133,29 @@ public class ProviderHelper {
public HashMap<String, Object> getUnifiedData(long masterKeyId, String[] proj, int[] types) public HashMap<String, Object> getUnifiedData(long masterKeyId, String[] proj, int[] types)
throws NotFoundException { throws NotFoundException {
return getGenericData(KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)), proj, types); return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
} }
/** private LongSparseArray<UncachedPublicKey> getUncachedMasterKeys(Uri queryUri) {
* Find the master key id related to a given query. The id will either be extracted from the
* query, which should work for all specific /key_rings/ queries, or will be queried if it can't.
*/
public long extractOrGetMasterKeyId(Uri queryUri)
throws NotFoundException {
// try extracting from the uri first
String firstSegment = queryUri.getPathSegments().get(1);
if (!firstSegment.equals("find")) try {
return Long.parseLong(firstSegment);
} catch (NumberFormatException e) {
// didn't work? oh well.
Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
}
return getMasterKeyId(queryUri);
}
public long getMasterKeyId(Uri queryUri) throws NotFoundException {
Object data = getGenericData(queryUri, KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER);
if (data != null) {
return (Long) data;
} else {
throw new NotFoundException();
}
}
public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) {
Cursor cursor = mContentResolver.query(queryUri, Cursor cursor = mContentResolver.query(queryUri,
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA}, new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
null, null, null); null, null, null);
LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount()); LongSparseArray<UncachedPublicKey> result =
new LongSparseArray<UncachedPublicKey>(cursor.getCount());
try { try {
if (cursor != null && cursor.moveToFirst()) do { if (cursor != null && cursor.moveToFirst()) do {
long masterKeyId = cursor.getLong(0); long masterKeyId = cursor.getLong(0);
byte[] data = cursor.getBlob(1); byte[] data = cursor.getBlob(1);
if (data != null) { if (data != null) {
result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data)); try {
result.put(masterKeyId,
UncachedKeyRing.decodePublicFromData(data).getPublicKey());
} catch(PgpGeneralException e) {
Log.e(Constants.TAG, "Error parsing keyring, skipping.");
} catch(IOException e) {
Log.e(Constants.TAG, "IO error, skipping keyring");
}
} }
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} finally { } finally {
@ -193,57 +167,74 @@ public class ProviderHelper {
return result; return result;
} }
public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException { public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) {
LongSparseArray<PGPKeyRing> result = getPGPKeyRings(queryUri); return new CachedPublicKeyRing(this, queryUri);
if (result.size() == 0) { }
throw new NotFoundException("PGPKeyRing object not found!");
} else { public WrappedPublicKeyRing getWrappedPublicKeyRing(long id) throws NotFoundException {
return result.valueAt(0); return (WrappedPublicKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false);
}
public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri queryUri) throws NotFoundException {
return (WrappedPublicKeyRing) getWrappedKeyRing(queryUri, false);
}
public WrappedSecretKeyRing getWrappedSecretKeyRing(long id) throws NotFoundException {
return (WrappedSecretKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true);
}
public WrappedSecretKeyRing getWrappedSecretKeyRing(Uri queryUri) throws NotFoundException {
return (WrappedSecretKeyRing) getWrappedKeyRing(queryUri, true);
}
private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{
// we pick from cache only information that is not easily available from keyrings
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED,
// and of course, ring data
secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA
}, null, null, null
);
try {
if (cursor != null && cursor.moveToFirst()) {
boolean hasAnySecret = cursor.getInt(0) > 0;
int verified = cursor.getInt(1);
byte[] blob = cursor.getBlob(2);
if(secret &! hasAnySecret) {
throw new NotFoundException("Secret key not available!");
}
return secret
? new WrappedSecretKeyRing(blob, hasAnySecret, verified)
: new WrappedPublicKeyRing(blob, hasAnySecret, verified);
} else {
throw new NotFoundException("Key not found!");
}
} finally {
if (cursor != null) {
cursor.close();
}
} }
} }
public PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(long keyId)
throws NotFoundException {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
long masterKeyId = getMasterKeyId(uri);
return getPGPPublicKeyRing(masterKeyId);
}
public PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(long keyId)
throws NotFoundException {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
long masterKeyId = getMasterKeyId(uri);
return getPGPSecretKeyRing(masterKeyId);
}
/**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
*/
public PGPPublicKeyRing getPGPPublicKeyRing(long masterKeyId) throws NotFoundException {
Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
return (PGPPublicKeyRing) getPGPKeyRing(queryUri);
}
/**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
*/
public PGPSecretKeyRing getPGPSecretKeyRing(long masterKeyId) throws NotFoundException {
Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
return (PGPSecretKeyRing) getPGPKeyRing(queryUri);
}
/** /**
* Saves PGPPublicKeyRing with its keys and userIds in DB * Saves PGPPublicKeyRing with its keys and userIds in DB
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void saveKeyRing(PGPPublicKeyRing keyRing) throws IOException { public void savePublicKeyRing(UncachedKeyRing keyRing) throws IOException {
PGPPublicKey masterKey = keyRing.getPublicKey(); if (keyRing.isSecret()) {
long masterKeyId = masterKey.getKeyID(); throw new RuntimeException("Tried to save secret keyring as public! " +
"This is a bug, please file a bug report.");
}
UncachedPublicKey masterKey = keyRing.getPublicKey();
long masterKeyId = masterKey.getKeyId();
// IF there is a secret key, preserve it! // IF there is a secret key, preserve it!
PGPSecretKeyRing secretRing = null; UncachedKeyRing secretRing = null;
try { try {
secretRing = getPGPSecretKeyRing(masterKeyId); secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached();
} catch (NotFoundException e) { } catch (NotFoundException e) {
Log.e(Constants.TAG, "key not found!"); Log.e(Constants.TAG, "key not found!");
} }
@ -266,36 +257,38 @@ public class ProviderHelper {
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
int rank = 0; int rank = 0;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) { for (UncachedPublicKey key : new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) {
operations.add(buildPublicKeyOperations(masterKeyId, key, rank)); operations.add(buildPublicKeyOperations(masterKeyId, key, rank));
++rank; ++rank;
} }
// get a list of owned secret keys, for verification filtering // get a list of owned secret keys, for verification filtering
LongSparseArray<PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri()); LongSparseArray<UncachedPublicKey> allKeyRings =
getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri());
// special case: available secret keys verify themselves! // special case: available secret keys verify themselves!
if (secretRing != null) if (secretRing != null) {
allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing); allKeyRings.put(secretRing.getMasterKeyId(), secretRing.getPublicKey());
}
// classify and order user ids. primary are moved to the front, revoked to the back, // classify and order user ids. primary are moved to the front, revoked to the back,
// otherwise the order in the keyfile is preserved. // otherwise the order in the keyfile is preserved.
List<UserIdItem> uids = new ArrayList<UserIdItem>(); List<UserIdItem> uids = new ArrayList<UserIdItem>();
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) { for (String userId : new IterableIterator<String>(
masterKey.getUnorderedUserIds().iterator())) {
UserIdItem item = new UserIdItem(); UserIdItem item = new UserIdItem();
uids.add(item); uids.add(item);
item.userId = userId; item.userId = userId;
// look through signatures for this specific key // look through signatures for this specific key
for (PGPSignature cert : new IterableIterator<PGPSignature>( for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
masterKey.getSignaturesForID(userId))) { masterKey.getSignaturesForId(userId))) {
long certId = cert.getKeyID(); long certId = cert.getKeyId();
try { try {
// self signature // self signature
if (certId == masterKeyId) { if (certId == masterKeyId) {
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider( cert.init(masterKey);
Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey); if (!cert.verifySignature(masterKey, userId)) {
if (!cert.verifyCertification(userId, masterKey)) {
// not verified?! dang! TODO notify user? this is kinda serious... // not verified?! dang! TODO notify user? this is kinda serious...
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!"); Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
continue; continue;
@ -304,31 +297,22 @@ public class ProviderHelper {
if (item.selfCert == null || if (item.selfCert == null ||
item.selfCert.getCreationTime().before(cert.getCreationTime())) { item.selfCert.getCreationTime().before(cert.getCreationTime())) {
item.selfCert = cert; item.selfCert = cert;
item.isPrimary = cert.getHashedSubPackets().isPrimaryUserID(); item.isPrimary = cert.isPrimaryUserId();
item.isRevoked = item.isRevoked = cert.isRevocation();
cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
} }
} }
// verify signatures from known private keys // verify signatures from known private keys
if (allKeyRings.indexOfKey(certId) >= 0) { if (allKeyRings.indexOfKey(certId) >= 0) {
// mark them as verified cert.init(allKeyRings.get(certId));
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider( if (cert.verifySignature(masterKey, userId)) {
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
allKeyRings.get(certId).getPublicKey());
if (cert.verifyCertification(userId, masterKey)) {
item.trustedCerts.add(cert); item.trustedCerts.add(cert);
} }
} }
} catch (SignatureException e) { } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "Signature verification failed! " Log.e(Constants.TAG, "Signature verification failed! "
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID()) + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyId())
+ " from " + " from "
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e); + PgpKeyHelper.convertKeyIdToHex(cert.getKeyId()), e);
} catch (PGPException e) {
Log.e(Constants.TAG, "Signature verification failed! "
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
+ " from "
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e);
} }
} }
} }
@ -365,7 +349,7 @@ public class ProviderHelper {
// Save the saved keyring (if any) // Save the saved keyring (if any)
if (secretRing != null) { if (secretRing != null) {
saveKeyRing(secretRing); saveSecretKeyRing(secretRing);
} }
} }
@ -374,8 +358,8 @@ public class ProviderHelper {
String userId; String userId;
boolean isPrimary = false; boolean isPrimary = false;
boolean isRevoked = false; boolean isRevoked = false;
PGPSignature selfCert; WrappedSignature selfCert;
List<PGPSignature> trustedCerts = new ArrayList<PGPSignature>(); List<WrappedSignature> trustedCerts = new ArrayList<WrappedSignature>();
@Override @Override
public int compareTo(UserIdItem o) { public int compareTo(UserIdItem o) {
@ -395,8 +379,13 @@ public class ProviderHelper {
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
* is already in the database! * is already in the database!
*/ */
public void saveKeyRing(PGPSecretKeyRing keyRing) throws IOException { public void saveSecretKeyRing(UncachedKeyRing keyRing) throws IOException {
long masterKeyId = keyRing.getPublicKey().getKeyID(); if (!keyRing.isSecret()) {
throw new RuntimeException("Tried to save publkc keyring as secret! " +
"This is a bug, please file a bug report.");
}
long masterKeyId = keyRing.getMasterKeyId();
{ {
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
@ -408,14 +397,10 @@ public class ProviderHelper {
values.put(Keys.HAS_SECRET, 1); values.put(Keys.HAS_SECRET, 1);
// then, mark exactly the keys we have available // then, mark exactly the keys we have available
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) { for (Long sub : new IterableIterator<Long>(keyRing.getAvailableSubkeys().iterator())) {
S2K s2k = sub.getS2K(); mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] {
// Set to 1, except if the encryption type is GNU_DUMMY_S2K Long.toString(sub)
if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) { });
mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{
Long.toString(sub.getKeyID())
});
}
} }
// this implicitly leaves all keys which were not in the secret key ring // this implicitly leaves all keys which were not in the secret key ring
// with has_secret = 0 // with has_secret = 0
@ -436,39 +421,39 @@ public class ProviderHelper {
/** /**
* Saves (or updates) a pair of public and secret KeyRings in the database * Saves (or updates) a pair of public and secret KeyRings in the database
*/ */
public void saveKeyRing(PGPPublicKeyRing pubRing, PGPSecretKeyRing privRing) throws IOException { public void saveKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException {
long masterKeyId = pubRing.getPublicKey().getKeyID(); long masterKeyId = pubRing.getPublicKey().getKeyId();
// delete secret keyring (so it isn't unnecessarily saved by public-saveKeyRing below) // delete secret keyring (so it isn't unnecessarily saved by public-savePublicKeyRing below)
mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null); mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
// save public keyring // save public keyring
saveKeyRing(pubRing); savePublicKeyRing(pubRing);
saveKeyRing(privRing); saveSecretKeyRing(secRing);
} }
/** /**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/ */
private ContentProviderOperation private ContentProviderOperation
buildPublicKeyOperations(long masterKeyId, PGPPublicKey key, int rank) throws IOException { buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(Keys.MASTER_KEY_ID, masterKeyId); values.put(Keys.MASTER_KEY_ID, masterKeyId);
values.put(Keys.RANK, rank); values.put(Keys.RANK, rank);
values.put(Keys.KEY_ID, key.getKeyID()); values.put(Keys.KEY_ID, key.getKeyId());
values.put(Keys.KEY_SIZE, key.getBitStrength()); values.put(Keys.KEY_SIZE, key.getBitStrength());
values.put(Keys.ALGORITHM, key.getAlgorithm()); values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.FINGERPRINT, key.getFingerprint()); values.put(Keys.FINGERPRINT, key.getFingerprint());
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key))); values.put(Keys.CAN_CERTIFY, key.canCertify());
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key))); values.put(Keys.CAN_SIGN, key.canSign());
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key)); values.put(Keys.CAN_ENCRYPT, key.canEncrypt());
values.put(Keys.IS_REVOKED, key.isRevoked()); values.put(Keys.IS_REVOKED, key.maybeRevoked());
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000); values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000);
Date expiryDate = PgpKeyHelper.getExpiryDate(key); Date expiryDate = key.getExpiryTime();
if (expiryDate != null) { if (expiryDate != null) {
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
} }
@ -482,11 +467,12 @@ public class ProviderHelper {
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/ */
private ContentProviderOperation private ContentProviderOperation
buildCertOperations(long masterKeyId, int rank, PGPSignature cert, int verified) throws IOException { buildCertOperations(long masterKeyId, int rank, WrappedSignature cert, int verified)
throws IOException {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(Certs.MASTER_KEY_ID, masterKeyId); values.put(Certs.MASTER_KEY_ID, masterKeyId);
values.put(Certs.RANK, rank); values.put(Certs.RANK, rank);
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID()); values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyId());
values.put(Certs.TYPE, cert.getSignatureType()); values.put(Certs.TYPE, cert.getSignatureType());
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000); values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
values.put(Certs.VERIFIED, verified); values.put(Certs.VERIFIED, verified);
@ -514,23 +500,11 @@ public class ProviderHelper {
return ContentProviderOperation.newInsert(uri).withValues(values).build(); return ContentProviderOperation.newInsert(uri).withValues(values).build();
} }
private String getKeyRingAsArmoredString(byte[] data) throws IOException { private String getKeyRingAsArmoredString(byte[] data) throws IOException, PgpGeneralException {
Object keyRing = null; UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);
if (data != null) {
keyRing = PgpConversionHelper.BytesToPGPKeyRing(data);
}
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = new ArmoredOutputStream(bos); keyRing.encodeArmored(bos, PgpHelper.getFullVersion(mContext));
aos.setHeader("Version", PgpHelper.getFullVersion(mContext));
if (keyRing instanceof PGPSecretKeyRing) {
aos.write(((PGPSecretKeyRing) keyRing).getEncoded());
} else if (keyRing instanceof PGPPublicKeyRing) {
aos.write(((PGPPublicKeyRing) keyRing).getEncoded());
}
aos.close();
String armoredKey = bos.toString("UTF-8"); String armoredKey = bos.toString("UTF-8");
Log.d(Constants.TAG, "armoredKey:" + armoredKey); Log.d(Constants.TAG, "armoredKey:" + armoredKey);
@ -539,77 +513,12 @@ public class ProviderHelper {
} }
public String getKeyRingAsArmoredString(Uri uri) public String getKeyRingAsArmoredString(Uri uri)
throws NotFoundException, IOException { throws NotFoundException, IOException, PgpGeneralException {
byte[] data = (byte[]) getGenericData( byte[] data = (byte[]) getGenericData(
uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB); uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB);
return getKeyRingAsArmoredString(data); return getKeyRingAsArmoredString(data);
} }
/**
* TODO: currently not used, but will be needed to upload many keys at once!
*
* @param masterKeyIds
* @return
* @throws IOException
*/
public ArrayList<String> getKeyRingsAsArmoredString(long[] masterKeyIds)
throws IOException {
ArrayList<String> output = new ArrayList<String>();
if (masterKeyIds == null || masterKeyIds.length == 0) {
Log.e(Constants.TAG, "No master keys given!");
return output;
}
// Build a cursor for the selected masterKeyIds
Cursor cursor;
{
String inMasterKeyList = KeyRingData.MASTER_KEY_ID + " IN (";
for (int i = 0; i < masterKeyIds.length; ++i) {
if (i != 0) {
inMasterKeyList += ", ";
}
inMasterKeyList += DatabaseUtils.sqlEscapeString("" + masterKeyIds[i]);
}
inMasterKeyList += ")";
cursor = mContentResolver.query(KeyRingData.buildPublicKeyRingUri(), new String[]{
KeyRingData._ID, KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA
}, inMasterKeyList, null, null);
}
try {
if (cursor != null) {
int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
if (cursor.moveToFirst()) {
do {
Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
byte[] data = cursor.getBlob(dataCol);
// get actual keyring data blob and write it to ByteArrayOutputStream
try {
output.add(getKeyRingAsArmoredString(data));
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
}
} while (cursor.moveToNext());
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
if (output.size() > 0) {
return output;
} else {
return null;
}
}
public ArrayList<String> getRegisteredApiApps() { public ArrayList<String> getRegisteredApiApps() {
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null); Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);

View File

@ -419,14 +419,14 @@ public class OpenPgpService extends RemoteService {
try { try {
// try to find key, throws NotFoundException if not in db! // try to find key, throws NotFoundException if not in db!
mProviderHelper.getPGPPublicKeyRing(masterKeyId); mProviderHelper.getWrappedPublicKeyRing(masterKeyId);
Intent result = new Intent(); Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
// also return PendingIntent that opens the key view activity // also return PendingIntent that opens the key view activity
Intent intent = new Intent(getBaseContext(), ViewKeyActivity.class); Intent intent = new Intent(getBaseContext(), ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(Long.toString(masterKeyId))); intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
intent, intent,

View File

@ -33,6 +33,7 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.ui.EditKeyActivity; import org.sufficientlysecure.keychain.ui.EditKeyActivity;
@ -179,9 +180,10 @@ public class AccountSettingsFragment extends Fragment implements
// select newly created key // select newly created key
try { try {
long masterKeyId = new ProviderHelper(getActivity()) long masterKeyId = new ProviderHelper(getActivity())
.extractOrGetMasterKeyId(data.getData()); .getCachedPublicKeyRing(data.getData())
.extractOrGetMasterKeyId();
mSelectKeyFragment.selectKey(masterKeyId); mSelectKeyFragment.selectKey(masterKeyId);
} catch (ProviderHelper.NotFoundException e) { } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
} }
} }

View File

@ -26,29 +26,25 @@ import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpImportExport; import org.sufficientlysecure.keychain.pgp.PgpImportExport;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@ -60,7 +56,6 @@ import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@ -497,7 +492,7 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_SAVE_KEYRING.equals(action)) { } else if (ACTION_SAVE_KEYRING.equals(action)) {
try { try {
/* Input */ /* Input */
SaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL); OldSaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL);
String oldPassphrase = saveParcel.oldPassphrase; String oldPassphrase = saveParcel.oldPassphrase;
String newPassphrase = saveParcel.newPassphrase; String newPassphrase = saveParcel.newPassphrase;
boolean canSign = true; boolean canSign = true;
@ -510,33 +505,36 @@ public class KeychainIntentService extends IntentService
newPassphrase = oldPassphrase; newPassphrase = oldPassphrase;
} }
long masterKeyId = saveParcel.keys.get(0).getKeyID(); long masterKeyId = saveParcel.keys.get(0).getKeyId();
/* Operation */ /* Operation */
ProviderHelper providerHelper = new ProviderHelper(this); ProviderHelper providerHelper = new ProviderHelper(this);
if (!canSign) { if (!canSign) {
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100)); setProgress(R.string.progress_building_key, 0, 100);
PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRing(masterKeyId); WrappedSecretKeyRing keyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
keyRing = keyOperations.changeSecretKeyPassphrase(keyRing, UncachedKeyRing newKeyRing =
oldPassphrase, newPassphrase); keyRing.changeSecretKeyPassphrase(oldPassphrase, newPassphrase);
setProgress(R.string.progress_saving_key_ring, 50, 100); setProgress(R.string.progress_saving_key_ring, 50, 100);
providerHelper.saveKeyRing(keyRing); providerHelper.saveSecretKeyRing(newKeyRing);
setProgress(R.string.progress_done, 100, 100); setProgress(R.string.progress_done, 100, 100);
} else { } else {
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100)); PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
PgpKeyOperation.Pair<PGPSecretKeyRing, PGPPublicKeyRing> pair;
try { try {
PGPSecretKeyRing privkey = providerHelper.getPGPSecretKeyRing(masterKeyId); WrappedSecretKeyRing seckey = providerHelper.getWrappedSecretKeyRing(masterKeyId);
PGPPublicKeyRing pubkey = providerHelper.getPGPPublicKeyRing(masterKeyId); WrappedPublicKeyRing pubkey = providerHelper.getWrappedPublicKeyRing(masterKeyId);
pair = keyOperations.buildSecretKey(privkey, pubkey, saveParcel); // edit existing PgpKeyOperation.Pair<UncachedKeyRing,UncachedKeyRing> pair =
keyOperations.buildSecretKey(seckey, pubkey, saveParcel); // edit existing
setProgress(R.string.progress_saving_key_ring, 90, 100);
providerHelper.saveKeyRing(pair.first, pair.second);
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
pair = keyOperations.buildNewSecretKey(saveParcel); //new Keyring PgpKeyOperation.Pair<UncachedKeyRing,UncachedKeyRing> pair =
keyOperations.buildNewSecretKey(saveParcel); //new Keyring
// save the pair
setProgress(R.string.progress_saving_key_ring, 90, 100);
providerHelper.saveKeyRing(pair.first, pair.second);
} }
setProgress(R.string.progress_saving_key_ring, 90, 100);
// save the pair
providerHelper.saveKeyRing(pair.second, pair.first);
setProgress(R.string.progress_done, 100, 100); setProgress(R.string.progress_done, 100, 100);
} }
PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase); PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase);
@ -556,13 +554,11 @@ public class KeychainIntentService extends IntentService
/* Operation */ /* Operation */
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey newKey = keyOperations.createKey(algorithm, keysize, byte[] newKey = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);
passphrase, masterKey);
/* Output */ /* Output */
Bundle resultData = new Bundle(); Bundle resultData = new Bundle();
resultData.putByteArray(RESULT_NEW_KEY, resultData.putByteArray(RESULT_NEW_KEY, newKey);
PgpConversionHelper.PGPSecretKeyToBytes(newKey));
OtherHelper.logDebugBundle(resultData, "resultData"); OtherHelper.logDebugBundle(resultData, "resultData");
@ -575,7 +571,6 @@ public class KeychainIntentService extends IntentService
try { try {
/* Input */ /* Input */
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
ArrayList<PGPSecretKey> newKeys = new ArrayList<PGPSecretKey>();
ArrayList<Integer> keyUsageList = new ArrayList<Integer>(); ArrayList<Integer> keyUsageList = new ArrayList<Integer>();
/* Operation */ /* Operation */
@ -588,24 +583,28 @@ public class KeychainIntentService extends IntentService
keysTotal); keysTotal);
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey masterKey = keyOperations.createKey(Constants.choice.algorithm.rsa, ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buf;
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, true); 4096, passphrase, true);
newKeys.add(masterKey); os.write(buf);
keyUsageList.add(KeyFlags.CERTIFY_OTHER); keyUsageList.add(UncachedSecretKey.CERTIFY_OTHER);
keysCreated++; keysCreated++;
setProgress(keysCreated, keysTotal); setProgress(keysCreated, keysTotal);
PGPSecretKey subKey = keyOperations.createKey(Constants.choice.algorithm.rsa, buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false); 4096, passphrase, false);
newKeys.add(subKey); os.write(buf);
keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE); keyUsageList.add(UncachedSecretKey.ENCRYPT_COMMS | UncachedSecretKey.ENCRYPT_STORAGE);
keysCreated++; keysCreated++;
setProgress(keysCreated, keysTotal); setProgress(keysCreated, keysTotal);
subKey = keyOperations.createKey(Constants.choice.algorithm.rsa, buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false); 4096, passphrase, false);
newKeys.add(subKey); os.write(buf);
keyUsageList.add(KeyFlags.SIGN_DATA); keyUsageList.add(UncachedSecretKey.SIGN_DATA);
keysCreated++; keysCreated++;
setProgress(keysCreated, keysTotal); setProgress(keysCreated, keysTotal);
@ -613,10 +612,8 @@ public class KeychainIntentService extends IntentService
// for sign // for sign
/* Output */ /* Output */
Bundle resultData = new Bundle(); Bundle resultData = new Bundle();
resultData.putByteArray(RESULT_NEW_KEY, resultData.putByteArray(RESULT_NEW_KEY, os.toByteArray());
PgpConversionHelper.PGPSecretKeyArrayListToBytes(newKeys));
resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList); resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList);
OtherHelper.logDebugBundle(resultData, "resultData"); OtherHelper.logDebugBundle(resultData, "resultData");
@ -648,12 +645,10 @@ public class KeychainIntentService extends IntentService
} }
} else if (ACTION_IMPORT_KEYRING.equals(action)) { } else if (ACTION_IMPORT_KEYRING.equals(action)) {
try { try {
List<ImportKeysListEntry> entries = data.getParcelableArrayList(IMPORT_KEY_LIST); List<ParcelableKeyRing> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
Bundle resultData = new Bundle();
PgpImportExport pgpImportExport = new PgpImportExport(this, this); PgpImportExport pgpImportExport = new PgpImportExport(this, this);
resultData = pgpImportExport.importKeyRings(entries); Bundle resultData = pgpImportExport.importKeyRings(entries);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) { } catch (Exception e) {
@ -727,15 +722,12 @@ public class KeychainIntentService extends IntentService
HkpKeyserver server = new HkpKeyserver(keyServer); HkpKeyserver server = new HkpKeyserver(keyServer);
ProviderHelper providerHelper = new ProviderHelper(this); ProviderHelper providerHelper = new ProviderHelper(this);
PGPPublicKeyRing keyring = (PGPPublicKeyRing) providerHelper.getPGPKeyRing(dataUri); WrappedPublicKeyRing keyring = providerHelper.getWrappedPublicKeyRing(dataUri);
if (keyring != null) { PgpImportExport pgpImportExport = new PgpImportExport(this, null);
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring);
(PGPPublicKeyRing) keyring); if (!uploaded) {
if (!uploaded) { throw new PgpGeneralException("Unable to export key to selected server");
throw new PgpGeneralException("Unable to export key to selected server");
}
} }
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
@ -747,40 +739,21 @@ public class KeychainIntentService extends IntentService
try { try {
KeybaseKeyserver server = new KeybaseKeyserver(); KeybaseKeyserver server = new KeybaseKeyserver();
ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
for (ImportKeysListEntry entry : entries) { for (ImportKeysListEntry entry : entries) {
// the keybase handle is in userId(1) // the keybase handle is in userId(1)
String keybaseId = entry.getExtraData(); String keybaseId = entry.getExtraData();
byte[] downloadedKeyBytes = server.get(keybaseId).getBytes(); byte[] downloadedKeyBytes = server.get(keybaseId).getBytes();
// create PGPKeyRing object based on downloaded armored key
PGPKeyRing downloadedKey = null;
BufferedInputStream bufferedInput =
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
if (bufferedInput.available() > 0) {
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
// get first object in block
Object obj;
if ((obj = objectFactory.nextObject()) != null) {
if (obj instanceof PGPKeyRing) {
downloadedKey = (PGPKeyRing) obj;
} else {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
}
// save key bytes in entry object for doing the // save key bytes in entry object for doing the
// actual import afterwards // actual import afterwards
entry.setBytes(downloadedKey.getEncoded()); keyRings.add(new ParcelableKeyRing(downloadedKeyBytes));
} }
Intent importIntent = new Intent(this, KeychainIntentService.class); Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING); importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle(); Bundle importData = new Bundle();
importData.putParcelableArrayList(IMPORT_KEY_LIST, entries); importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
importIntent.putExtra(EXTRA_DATA, importData); importIntent.putExtra(EXTRA_DATA, importData);
importIntent.putExtra(EXTRA_MESSENGER, mMessenger); importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
@ -794,11 +767,12 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action)) { } else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action)) {
try { try {
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST); ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
// this downloads the keys and places them into the ImportKeysListEntry entries // this downloads the keys and places them into the ImportKeysListEntry entries
String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
HkpKeyserver server = new HkpKeyserver(keyServer); HkpKeyserver server = new HkpKeyserver(keyServer);
ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
for (ImportKeysListEntry entry : entries) { for (ImportKeysListEntry entry : entries) {
// if available use complete fingerprint for get request // if available use complete fingerprint for get request
byte[] downloadedKeyBytes; byte[] downloadedKeyBytes;
@ -808,49 +782,15 @@ public class KeychainIntentService extends IntentService
downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes(); downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
} }
// create PGPKeyRing object based on downloaded armored key
PGPKeyRing downloadedKey = null;
BufferedInputStream bufferedInput =
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
if (bufferedInput.available() > 0) {
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
// get first object in block
Object obj;
if ((obj = objectFactory.nextObject()) != null) {
if (obj instanceof PGPKeyRing) {
downloadedKey = (PGPKeyRing) obj;
} else {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
}
// verify downloaded key by comparing fingerprints
if (entry.getFingerprintHex() != null) {
String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
downloadedKey.getPublicKey().getFingerprint());
if (downloadedKeyFp.equals(entry.getFingerprintHex())) {
Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " +
"the requested fingerprint!");
} else {
throw new PgpGeneralException("fingerprint of downloaded key is " +
"NOT the same as the requested fingerprint!");
}
}
// save key bytes in entry object for doing the // save key bytes in entry object for doing the
// actual import afterwards // actual import afterwards
entry.setBytes(downloadedKey.getEncoded()); keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex()));
} }
Intent importIntent = new Intent(this, KeychainIntentService.class); Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING); importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle(); Bundle importData = new Bundle();
importData.putParcelableArrayList(IMPORT_KEY_LIST, entries); importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
importIntent.putExtra(EXTRA_DATA, importData); importIntent.putExtra(EXTRA_DATA, importData);
importIntent.putExtra(EXTRA_MESSENGER, mMessenger); importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
@ -877,29 +817,18 @@ public class KeychainIntentService extends IntentService
} }
ProviderHelper providerHelper = new ProviderHelper(this); ProviderHelper providerHelper = new ProviderHelper(this);
PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); WrappedPublicKeyRing publicRing = providerHelper.getWrappedPublicKeyRing(pubKeyId);
PGPPublicKeyRing publicRing = providerHelper.getPGPPublicKeyRing(pubKeyId); WrappedSecretKeyRing secretKeyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId); WrappedSecretKey certificationKey = secretKeyRing.getSubKey();
PGPSecretKeyRing secretKeyRing = null; if(!certificationKey.unlock(signaturePassphrase)) {
try { throw new PgpGeneralException("Error extracting key (bad passphrase?)");
secretKeyRing = providerHelper.getPGPSecretKeyRing(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
// TODO: throw exception here!
} }
PGPSecretKey certificationKey = PgpKeyHelper.getFirstCertificationSubkey(secretKeyRing); UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds);
publicKey = keyOperation.certifyKey(certificationKey, publicKey,
userIds, signaturePassphrase);
publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);
// store the signed key in our local cache // store the signed key in our local cache
PgpImportExport pgpImportExport = new PgpImportExport(this, null); providerHelper.savePublicKeyRing(newRing);
int retval = pgpImportExport.storeKeyRingInCache(publicRing);
if (retval != PgpImportExport.RETURN_OK && retval != PgpImportExport.RETURN_UPDATED) {
throw new PgpGeneralException("Failed to store signed key in local cache");
}
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
} catch (Exception e) { } catch (Exception e) {
sendErrorToHandler(e); sendErrorToHandler(e);
} }

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.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
* 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.service;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
/** Class for parcelling data between ui and services.
* This class is outdated and scheduled for removal, pending a rewrite of the
* EditKeyActivity and save keyring routines.
*/
@Deprecated
public class OldSaveKeyringParcel implements Parcelable {
public ArrayList<String> userIds;
public ArrayList<String> originalIDs;
public ArrayList<String> deletedIDs;
public boolean[] newIDs;
public boolean primaryIDChanged;
public boolean[] moddedKeys;
public ArrayList<UncachedSecretKey> deletedKeys;
public ArrayList<Calendar> keysExpiryDates;
public ArrayList<Integer> keysUsages;
public String newPassphrase;
public String oldPassphrase;
public boolean[] newKeys;
public ArrayList<UncachedSecretKey> keys;
public String originalPrimaryID;
public OldSaveKeyringParcel() {}
private OldSaveKeyringParcel(Parcel source) {
userIds = (ArrayList<String>) source.readSerializable();
originalIDs = (ArrayList<String>) source.readSerializable();
deletedIDs = (ArrayList<String>) source.readSerializable();
newIDs = source.createBooleanArray();
primaryIDChanged = source.readByte() != 0;
moddedKeys = source.createBooleanArray();
byte[] tmp = source.createByteArray();
if (tmp == null) {
deletedKeys = null;
} else {
deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp);
}
keysExpiryDates = (ArrayList<Calendar>) source.readSerializable();
keysUsages = source.readArrayList(Integer.class.getClassLoader());
newPassphrase = source.readString();
oldPassphrase = source.readString();
newKeys = source.createBooleanArray();
keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray());
originalPrimaryID = source.readString();
}
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeSerializable(userIds); //might not be the best method to store.
destination.writeSerializable(originalIDs);
destination.writeSerializable(deletedIDs);
destination.writeBooleanArray(newIDs);
destination.writeByte((byte) (primaryIDChanged ? 1 : 0));
destination.writeBooleanArray(moddedKeys);
destination.writeByteArray(encodeArrayList(deletedKeys));
destination.writeSerializable(keysExpiryDates);
destination.writeList(keysUsages);
destination.writeString(newPassphrase);
destination.writeString(oldPassphrase);
destination.writeBooleanArray(newKeys);
destination.writeByteArray(encodeArrayList(keys));
destination.writeString(originalPrimaryID);
}
public static final Creator<OldSaveKeyringParcel> CREATOR = new Creator<OldSaveKeyringParcel>() {
public OldSaveKeyringParcel createFromParcel(final Parcel source) {
return new OldSaveKeyringParcel(source);
}
public OldSaveKeyringParcel[] newArray(final int size) {
return new OldSaveKeyringParcel[size];
}
};
private static byte[] encodeArrayList(ArrayList<UncachedSecretKey> list) {
if(list.isEmpty()) {
return null;
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
for(UncachedSecretKey key : new IterableIterator<UncachedSecretKey>(list.iterator())) {
try {
key.encodeSecretKey(os);
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting ArrayList<UncachedSecretKey> to byte[]!", e);
}
}
return os.toByteArray();
}
@Override
public int describeContents() {
return 0;
}
}

View File

@ -34,20 +34,14 @@ import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v4.util.LongSparseArray; import android.support.v4.util.LongSparseArray;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.util.Date; import java.util.Date;
import java.util.Iterator;
/** /**
* This service runs in its own process, but is available to all other processes as the main * This service runs in its own process, but is available to all other processes as the main
@ -163,81 +157,47 @@ public class PassphraseCacheService extends Service {
* @return * @return
*/ */
private String getCachedPassphraseImpl(long keyId) { private String getCachedPassphraseImpl(long keyId) {
Log.d(TAG, "getCachedPassphraseImpl() get masterKeyId for " + keyId); // passphrase for symmetric encryption?
if (keyId == Constants.key.symmetric) {
Log.d(TAG, "getCachedPassphraseImpl() for symmetric encryption");
String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric);
if (cachedPassphrase == null) {
return null;
}
addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase);
return cachedPassphrase;
}
// try to get master key id which is used as an identifier for cached passphrases // try to get master key id which is used as an identifier for cached passphrases
long masterKeyId = keyId; try {
if (masterKeyId != Constants.key.symmetric) { Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + keyId);
try { WrappedSecretKeyRing key = new ProviderHelper(this).getWrappedSecretKeyRing(
masterKeyId = new ProviderHelper(this).getMasterKeyId( KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId));
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId))); // no passphrase needed? just add empty string and return it, then
} catch (ProviderHelper.NotFoundException e) { if (!key.hasPassphrase()) {
return null;
}
}
Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + masterKeyId);
// get cached passphrase
String cachedPassphrase = mPassphraseCache.get(masterKeyId);
if (cachedPassphrase == null) {
// if key has no passphrase -> cache and return empty passphrase
if (!hasPassphrase(this, masterKeyId)) {
Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!"); Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!");
addCachedPassphrase(this, masterKeyId, ""); addCachedPassphrase(this, keyId, "");
return ""; return "";
} else { }
// get cached passphrase
String cachedPassphrase = mPassphraseCache.get(keyId);
if (cachedPassphrase == null) {
Log.d(TAG, "Passphrase not (yet) cached, returning null");
// not really an error, just means the passphrase is not cached but not empty either
return null; return null;
} }
}
// set it again to reset the cache life cycle
Log.d(TAG, "Cache passphrase again when getting it!");
addCachedPassphrase(this, masterKeyId, cachedPassphrase);
return cachedPassphrase; // set it again to reset the cache life cycle
} Log.d(TAG, "Cache passphrase again when getting it!");
addCachedPassphrase(this, keyId, cachedPassphrase);
return cachedPassphrase;
public static boolean hasPassphrase(PGPSecretKeyRing secretKeyRing) {
PGPSecretKey secretKey = null;
boolean foundValidKey = false;
for (Iterator keys = secretKeyRing.getSecretKeys(); keys.hasNext(); ) {
secretKey = (PGPSecretKey) keys.next();
if (!secretKey.isPrivateKeyEmpty()) {
foundValidKey = true;
break;
}
}
if(!foundValidKey) {
return false;
}
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider("SC").build("".toCharArray());
PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
return testKey == null;
} catch(PGPException e) {
// this means the crc check failed -> passphrase required
return true;
}
}
/**
* Checks if key has a passphrase.
*
* @param secretKeyId
* @return true if it has a passphrase
*/
public static boolean hasPassphrase(Context context, long secretKeyId) {
// check if the key has no passphrase
try {
PGPSecretKeyRing secRing = new ProviderHelper(context).getPGPSecretKeyRing(secretKeyId);
return hasPassphrase(secRing);
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(TAG, "Passphrase for unknown key was requested!");
return null;
} }
return true;
} }
/** /**

View File

@ -1,92 +1,101 @@
/*
* Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.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
* 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.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.spongycastle.openpgp.PGPSecretKey; import java.io.Serializable;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import java.util.HashMap;
import java.util.ArrayList;
import java.util.Calendar;
/** This class is a a transferable representation for a collection of changes
* to be done on a keyring.
*
* This class should include all types of operations supported in the backend.
*
* All changes are done in a differential manner. Besides the two key
* identification attributes, all attributes may be null, which indicates no
* change to the keyring. This is also the reason why boxed values are used
* instead of primitives in the subclasses.
*
* Application of operations in the backend should be fail-fast, which means an
* error in any included operation (for example revocation of a non-existent
* subkey) will cause the operation as a whole to fail.
*/
public class SaveKeyringParcel implements Parcelable { public class SaveKeyringParcel implements Parcelable {
public ArrayList<String> userIds; // the master key id to be edited
public ArrayList<String> originalIDs; private final long mMasterKeyId;
public ArrayList<String> deletedIDs; // the key fingerprint, for safety
public boolean[] newIDs; private final byte[] mFingerprint;
public boolean primaryIDChanged;
public boolean[] moddedKeys;
public ArrayList<PGPSecretKey> deletedKeys;
public ArrayList<Calendar> keysExpiryDates;
public ArrayList<Integer> keysUsages;
public String newPassphrase; public String newPassphrase;
public String oldPassphrase;
public boolean[] newKeys;
public ArrayList<PGPSecretKey> keys;
public String originalPrimaryID;
public SaveKeyringParcel() {} public String[] addUserIds;
public SubkeyAdd[] addSubKeys;
private SaveKeyringParcel(Parcel source) { public HashMap<Long, SubkeyChange> changeSubKeys;
userIds = (ArrayList<String>) source.readSerializable(); public String changePrimaryUserId;
originalIDs = (ArrayList<String>) source.readSerializable();
deletedIDs = (ArrayList<String>) source.readSerializable(); public String[] revokeUserIds;
newIDs = source.createBooleanArray(); public long[] revokeSubKeys;
primaryIDChanged = source.readByte() != 0;
moddedKeys = source.createBooleanArray(); public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) {
byte[] tmp = source.createByteArray(); mMasterKeyId = masterKeyId;
if (tmp == null) { mFingerprint = fingerprint;
deletedKeys = null; }
} else {
deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp); // performance gain for using Parcelable here would probably be negligible,
// use Serializable instead.
public static class SubkeyAdd implements Serializable {
public final int mAlgorithm;
public final int mKeysize;
public final int mFlags;
public final Long mExpiry;
public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) {
mAlgorithm = algorithm;
mKeysize = keysize;
mFlags = flags;
mExpiry = expiry;
} }
keysExpiryDates = (ArrayList<Calendar>) source.readSerializable(); }
keysUsages = source.readArrayList(Integer.class.getClassLoader());
newPassphrase = source.readString(); public static class SubkeyChange implements Serializable {
oldPassphrase = source.readString(); public final long mKeyId;
newKeys = source.createBooleanArray(); public final Integer mFlags;
keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray()); public final Long mExpiry;
originalPrimaryID = source.readString(); public SubkeyChange(long keyId, Integer flags, Long expiry) {
mKeyId = keyId;
mFlags = flags;
mExpiry = expiry;
}
}
public SaveKeyringParcel(Parcel source) {
mMasterKeyId = source.readLong();
mFingerprint = source.createByteArray();
addUserIds = source.createStringArray();
addSubKeys = (SubkeyAdd[]) source.readSerializable();
changeSubKeys = (HashMap<Long,SubkeyChange>) source.readSerializable();
changePrimaryUserId = source.readString();
revokeUserIds = source.createStringArray();
revokeSubKeys = source.createLongArray();
} }
@Override @Override
public void writeToParcel(Parcel destination, int flags) { public void writeToParcel(Parcel destination, int flags) {
destination.writeSerializable(userIds); //might not be the best method to store. destination.writeLong(mMasterKeyId);
destination.writeSerializable(originalIDs); destination.writeByteArray(mFingerprint);
destination.writeSerializable(deletedIDs);
destination.writeBooleanArray(newIDs); destination.writeStringArray(addUserIds);
destination.writeByte((byte) (primaryIDChanged ? 1 : 0)); destination.writeSerializable(addSubKeys);
destination.writeBooleanArray(moddedKeys);
byte[] tmp = null; destination.writeSerializable(changeSubKeys);
if (deletedKeys.size() != 0) { destination.writeString(changePrimaryUserId);
tmp = PgpConversionHelper.PGPSecretKeyArrayListToBytes(deletedKeys);
} destination.writeStringArray(revokeUserIds);
destination.writeByteArray(tmp); destination.writeLongArray(revokeSubKeys);
destination.writeSerializable(keysExpiryDates);
destination.writeList(keysUsages);
destination.writeString(newPassphrase);
destination.writeString(oldPassphrase);
destination.writeBooleanArray(newKeys);
destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys));
destination.writeString(originalPrimaryID);
} }
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() { public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
@ -103,4 +112,5 @@ public class SaveKeyringParcel implements Parcelable {
public int describeContents() { public int describeContents() {
return 0; return 0;
} }
} }

View File

@ -43,7 +43,6 @@ import android.widget.TextView;
import com.devspark.appmsg.AppMsg; import com.devspark.appmsg.AppMsg;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
@ -51,7 +50,6 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@ -222,54 +220,22 @@ public class CertifyKeyActivity extends ActionBarActivity implements
* handles the UI bits of the signing process on the UI thread * handles the UI bits of the signing process on the UI thread
*/ */
private void initiateSigning() { private void initiateSigning() {
try { // get the user's passphrase for this key (if required)
PGPPublicKeyRing pubring = new ProviderHelper(this).getPGPPublicKeyRing(mPubKeyId); String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
if (passphrase == null) {
// if we have already signed this key, dont bother doing it again PassphraseDialogFragment.show(this, mMasterKeyId,
boolean alreadySigned = false; new Handler() {
@Override
/* todo: reconsider this at a later point when certs are in the db public void handleMessage(Message message) {
@SuppressWarnings("unchecked") if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
Iterator<PGPSignature> itr = pubring.getPublicKey(mPubKeyId).getSignatures(); startSigning();
while (itr.hasNext()) {
PGPSignature sig = itr.next();
if (sig.getKeyID() == mMasterKeyId) {
alreadySigned = true;
break;
}
}
*/
if (!alreadySigned) {
/*
* get the user's passphrase for this key (if required)
*/
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
if (passphrase == null) {
PassphraseDialogFragment.show(this, mMasterKeyId,
new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
startSigning();
}
}
} }
); }
// bail out; need to wait until the user has entered the passphrase before trying again });
return; // bail out; need to wait until the user has entered the passphrase before trying again
} else { return;
startSigning(); } else {
} startSigning();
} else {
AppMsg.makeText(this, R.string.key_has_already_been_certified, AppMsg.STYLE_ALERT)
.show();
setResult(RESULT_CANCELED);
finish();
}
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
} }
} }

View File

@ -19,7 +19,6 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -44,21 +43,22 @@ import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton; import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg; import com.devspark.appmsg.AppMsg;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.ExportHelper; import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
@ -67,7 +67,6 @@ import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
import org.sufficientlysecure.keychain.ui.widget.KeyEditor; import org.sufficientlysecure.keychain.ui.widget.KeyEditor;
import org.sufficientlysecure.keychain.ui.widget.SectionView; import org.sufficientlysecure.keychain.ui.widget.SectionView;
import org.sufficientlysecure.keychain.ui.widget.UserIdEditor; import org.sufficientlysecure.keychain.ui.widget.UserIdEditor;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList; import java.util.ArrayList;
@ -89,8 +88,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// EDIT // EDIT
private Uri mDataUri; private Uri mDataUri;
private PGPSecretKeyRing mKeyRing = null;
private SectionView mUserIdsView; private SectionView mUserIdsView;
private SectionView mKeysView; private SectionView mKeysView;
@ -106,7 +103,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
private CheckBox mNoPassphrase; private CheckBox mNoPassphrase;
Vector<String> mUserIds; Vector<String> mUserIds;
Vector<PGPSecretKey> mKeys; Vector<UncachedSecretKey> mKeys;
Vector<Integer> mKeysUsages; Vector<Integer> mKeysUsages;
boolean mMasterCanSign = true; boolean mMasterCanSign = true;
@ -159,7 +156,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
); );
mUserIds = new Vector<String>(); mUserIds = new Vector<String>();
mKeys = new Vector<PGPSecretKey>(); mKeys = new Vector<UncachedSecretKey>();
mKeysUsages = new Vector<Integer>(); mKeysUsages = new Vector<Integer>();
// Catch Intents opened from other apps // Catch Intents opened from other apps
@ -240,7 +237,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// get new key from data bundle returned from service // get new key from data bundle returned from service
Bundle data = message.getData(); Bundle data = message.getData();
ArrayList<PGPSecretKey> newKeys = ArrayList<UncachedSecretKey> newKeys =
PgpConversionHelper.BytesToPGPSecretKeyList(data PgpConversionHelper.BytesToPGPSecretKeyList(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY)); .getByteArray(KeychainIntentService.RESULT_NEW_KEY));
@ -288,18 +285,18 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
Log.d(Constants.TAG, "uri: " + mDataUri); Log.d(Constants.TAG, "uri: " + mDataUri);
try { try {
Uri secretUri = KeychainContract.KeyRingData.buildSecretKeyRingUri(mDataUri); Uri secretUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
mKeyRing = (PGPSecretKeyRing) new ProviderHelper(this).getPGPKeyRing(secretUri); WrappedSecretKeyRing keyRing = new ProviderHelper(this).getWrappedSecretKeyRing(secretUri);
PGPSecretKey masterKey = mKeyRing.getSecretKey(); mMasterCanSign = keyRing.getSubKey().canCertify();
mMasterCanSign = PgpKeyHelper.isCertificationKey(mKeyRing.getSecretKey()); for (WrappedSecretKey key : keyRing.iterator()) {
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(mKeyRing.getSecretKeys())) { // Turn into uncached instance
mKeys.add(key); mKeys.add(key.getUncached());
mKeysUsages.add(-1); // get usage when view is created mKeysUsages.add(key.getKeyUsage()); // get usage when view is created
} }
boolean isSet = false; boolean isSet = false;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) { for (String userId : keyRing.getSubKey().getUserIds()) {
Log.d(Constants.TAG, "Added userId " + userId); Log.d(Constants.TAG, "Added userId " + userId);
if (!isSet) { if (!isSet) {
isSet = true; isSet = true;
@ -314,7 +311,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
buildLayout(false); buildLayout(false);
mCurrentPassphrase = ""; mCurrentPassphrase = "";
mIsPassphraseSet = PassphraseCacheService.hasPassphrase(mKeyRing); mIsPassphraseSet = keyRing.hasPassphrase();
if (!mIsPassphraseSet) { if (!mIsPassphraseSet) {
// check "no passphrase" checkbox and remove button // check "no passphrase" checkbox and remove button
mNoPassphrase.setChecked(true); mNoPassphrase.setChecked(true);
@ -432,7 +429,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
if (mKeysView.getEditors().getChildCount() == 0) { if (mKeysView.getEditors().getChildCount() == 0) {
return 0; return 0;
} }
return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyID(); return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyId();
} }
public boolean isPassphraseSet() { public boolean isPassphraseSet() {
@ -556,7 +553,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING); intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
SaveKeyringParcel saveParams = new SaveKeyringParcel(); OldSaveKeyringParcel saveParams = new OldSaveKeyringParcel();
saveParams.userIds = getUserIds(mUserIdsView); saveParams.userIds = getUserIds(mUserIdsView);
saveParams.originalIDs = mUserIdsView.getOriginalIDs(); saveParams.originalIDs = mUserIdsView.getOriginalIDs();
saveParams.deletedIDs = mUserIdsView.getDeletedIDs(); saveParams.deletedIDs = mUserIdsView.getDeletedIDs();
@ -572,7 +569,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
saveParams.keys = getKeys(mKeysView); saveParams.keys = getKeys(mKeysView);
saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID(); saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID();
// fill values for this action // fill values for this action
Bundle data = new Bundle(); Bundle data = new Bundle();
data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign); data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign);
@ -591,8 +587,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
Intent data = new Intent(); Intent data = new Intent();
// return uri pointing to new created key // return uri pointing to new created key
Uri uri = KeychainContract.KeyRings.buildGenericKeyRingUri( Uri uri = KeyRings.buildGenericKeyRingUri(getMasterKeyId());
String.valueOf(getMasterKeyId()));
data.setData(uri); data.setData(uri);
setResult(RESULT_OK, data); setResult(RESULT_OK, data);
@ -690,8 +685,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
* @param keysView * @param keysView
* @return * @return
*/ */
private ArrayList<PGPSecretKey> getKeys(SectionView keysView) throws PgpGeneralException { private ArrayList<UncachedSecretKey> getKeys(SectionView keysView) throws PgpGeneralException {
ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>(); ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
ViewGroup keyEditors = keysView.getEditors(); ViewGroup keyEditors = keysView.getEditors();

View File

@ -30,11 +30,11 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton; import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -144,20 +144,17 @@ public class EncryptAsymmetricFragment extends Fragment {
*/ */
private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds, private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds,
ProviderHelper providerHelper) { ProviderHelper providerHelper) {
// TODO all of this works under the assumption that the first suitable subkey is always used!
// not sure if we need to distinguish between different subkeys here?
if (preselectedSignatureKeyId != 0) { if (preselectedSignatureKeyId != 0) {
// TODO: don't use bouncy castle objects!
try { try {
PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRingWithKeyId( CachedPublicKeyRing keyring =
preselectedSignatureKeyId); providerHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(preselectedSignatureKeyId));
PGPSecretKey masterKey = keyRing.getSecretKey(); if(keyring.hasAnySecret()) {
if (masterKey != null) { setSignatureKeyId(keyring.getMasterKeyId());
PGPSecretKey signKey = PgpKeyHelper.getFirstSigningSubkey(keyRing);
if (signKey != null) {
setSignatureKeyId(masterKey.getKeyID());
}
} }
} catch (ProviderHelper.NotFoundException e) { } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
} }
} }
@ -165,14 +162,13 @@ public class EncryptAsymmetricFragment extends Fragment {
if (preselectedEncryptionKeyIds != null) { if (preselectedEncryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>(); Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) { for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
// TODO One query per selected key?! wtf
try { try {
long id = providerHelper.getMasterKeyId( long id = providerHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri( KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
Long.toString(preselectedEncryptionKeyIds[i])) preselectedEncryptionKeyIds[i])
); ).getMasterKeyId();
goodIds.add(id); goodIds.add(id);
} catch (ProviderHelper.NotFoundException e) { } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
} }
} }

View File

@ -42,6 +42,7 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@ -415,8 +416,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
// fill values for this action // fill values for this action
Bundle data = new Bundle(); Bundle data = new Bundle();
// get selected key entries // get DATA from selected key entries
ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData(); ArrayList<ParcelableKeyRing> selectedEntries = mListFragment.getSelectedData();
data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries); data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data); intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -442,7 +443,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, mListFragment.getKeyServer()); data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, mListFragment.getKeyServer());
// get selected key entries // get selected key entries
ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData(); ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries();
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries); data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data); intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -466,7 +467,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
Bundle data = new Bundle(); Bundle data = new Bundle();
// get selected key entries // get selected key entries
ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData(); ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries();
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries); data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data); intent.putExtra(KeychainIntentService.EXTRA_DATA, data);

View File

@ -23,6 +23,7 @@ import android.os.Bundle;
import android.support.v4.app.ListFragment; import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
import android.support.v4.util.LongSparseArray;
import android.view.View; import android.view.View;
import android.widget.ListView; import android.widget.ListView;
@ -31,6 +32,7 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper; import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
@ -67,6 +69,8 @@ public class ImportKeysListFragment extends ListFragment implements
private static final int LOADER_ID_SERVER_QUERY = 1; private static final int LOADER_ID_SERVER_QUERY = 1;
private static final int LOADER_ID_KEYBASE = 2; private static final int LOADER_ID_KEYBASE = 2;
private LongSparseArray<ParcelableKeyRing> mCachedKeyData;
public byte[] getKeyBytes() { public byte[] getKeyBytes() {
return mKeyBytes; return mKeyBytes;
} }
@ -91,8 +95,16 @@ public class ImportKeysListFragment extends ListFragment implements
return mAdapter.getData(); return mAdapter.getData();
} }
public ArrayList<ImportKeysListEntry> getSelectedData() { public ArrayList<ParcelableKeyRing> getSelectedData() {
return mAdapter.getSelectedData(); ArrayList<ParcelableKeyRing> result = new ArrayList<ParcelableKeyRing>();
for(ImportKeysListEntry entry : getSelectedEntries()) {
result.add(mCachedKeyData.get(entry.getKeyId()));
}
return result;
}
public ArrayList<ImportKeysListEntry> getSelectedEntries() {
return mAdapter.getSelectedEntries();
} }
/** /**
@ -120,8 +132,7 @@ public class ImportKeysListFragment extends ListFragment implements
mActivity = getActivity(); mActivity = getActivity();
// Give some text to display if there is no data. In a real // Give some text to display if there is no data.
// application this would come from a resource.
setEmptyText(mActivity.getString(R.string.error_nothing_import)); setEmptyText(mActivity.getString(R.string.error_nothing_import));
// Create an empty adapter we will use to display the loaded data. // Create an empty adapter we will use to display the loaded data.
@ -252,11 +263,15 @@ public class ImportKeysListFragment extends ListFragment implements
Exception error = data.getError(); Exception error = data.getError();
// free old cached key data
mCachedKeyData = null;
switch (loader.getId()) { switch (loader.getId()) {
case LOADER_ID_BYTES: case LOADER_ID_BYTES:
if (error == null) { if (error == null) {
// No error // No error
mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
} else if (error instanceof ImportKeysListLoader.FileHasNoContent) { } else if (error instanceof ImportKeysListLoader.FileHasNoContent) {
AppMsg.makeText(getActivity(), R.string.error_import_file_no_content, AppMsg.makeText(getActivity(), R.string.error_import_file_no_content,
AppMsg.STYLE_ALERT).show(); AppMsg.STYLE_ALERT).show();

View File

@ -317,7 +317,7 @@ public class KeyListFragment extends LoaderFragment
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class); Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
viewIntent.setData( viewIntent.setData(
KeyRings.buildGenericKeyRingUri(Long.toString(mAdapter.getMasterKeyId(position)))); KeyRings.buildGenericKeyRingUri(mAdapter.getMasterKeyId(position)));
startActivity(viewIntent); startActivity(viewIntent);
} }

View File

@ -84,7 +84,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
@Override @Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
long masterKeyId = mAdapter.getMasterKeyId(position); long masterKeyId = mAdapter.getMasterKeyId(position);
Uri result = KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId)); Uri result = KeyRings.buildGenericKeyRingUri(masterKeyId);
// return data to activity, which results in finishing it // return data to activity, which results in finishing it
mActivity.afterListSelection(result); mActivity.afterListSelection(result);

View File

@ -132,7 +132,7 @@ public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderMan
//For AppSettingsFragment //For AppSettingsFragment
public void selectKey(long masterKeyId) { public void selectKey(long masterKeyId) {
Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId)); Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(masterKeyId);
mReceivedUri = buildUri; mReceivedUri = buildUri;
getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this); getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
} }

View File

@ -32,24 +32,17 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import org.spongycastle.bcpg.SignatureSubpacket;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.RevocationReason;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.security.SignatureException;
import java.util.Date; import java.util.Date;
public class ViewCertActivity extends ActionBarActivity public class ViewCertActivity extends ActionBarActivity
@ -146,32 +139,25 @@ public class ViewCertActivity extends ActionBarActivity
mCertifierUid.setText(R.string.unknown_uid); mCertifierUid.setText(R.string.unknown_uid);
} }
PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(data.getBlob(INDEX_DATA)); WrappedSignature sig = WrappedSignature.fromBytes(data.getBlob(INDEX_DATA));
try { try {
ProviderHelper providerHelper = new ProviderHelper(this); ProviderHelper providerHelper = new ProviderHelper(this);
PGPKeyRing signeeRing = providerHelper.getPGPKeyRing(
KeychainContract.KeyRingData.buildPublicKeyRingUri( WrappedPublicKeyRing signeeRing =
Long.toString(data.getLong(INDEX_MASTER_KEY_ID))) providerHelper.getWrappedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID));
); WrappedPublicKeyRing signerRing =
PGPKeyRing signerRing = providerHelper.getPGPKeyRing( providerHelper.getWrappedPublicKeyRing(sig.getKeyId());
KeychainContract.KeyRingData.buildPublicKeyRingUri(
Long.toString(sig.getKeyID()))
);
try { try {
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider( sig.init(signerRing.getSubkey());
Constants.BOUNCY_CASTLE_PROVIDER_NAME), signerRing.getPublicKey()); if (sig.verifySignature(signeeRing.getSubkey(), signeeUid)) {
if (sig.verifyCertification(signeeUid, signeeRing.getPublicKey())) {
mStatus.setText(R.string.cert_verify_ok); mStatus.setText(R.string.cert_verify_ok);
mStatus.setTextColor(getResources().getColor(R.color.bbutton_success)); mStatus.setTextColor(getResources().getColor(R.color.bbutton_success));
} else { } else {
mStatus.setText(R.string.cert_verify_failed); mStatus.setText(R.string.cert_verify_failed);
mStatus.setTextColor(getResources().getColor(R.color.alert)); mStatus.setTextColor(getResources().getColor(R.color.alert));
} }
} catch (SignatureException e) { } catch (PgpGeneralException e) {
mStatus.setText(R.string.cert_verify_error);
mStatus.setTextColor(getResources().getColor(R.color.alert));
} catch (PGPException e) {
mStatus.setText(R.string.cert_verify_error); mStatus.setText(R.string.cert_verify_error);
mStatus.setTextColor(getResources().getColor(R.color.alert)); mStatus.setTextColor(getResources().getColor(R.color.alert));
} }
@ -185,29 +171,26 @@ public class ViewCertActivity extends ActionBarActivity
mRowReason.setVisibility(View.GONE); mRowReason.setVisibility(View.GONE);
switch (data.getInt(INDEX_TYPE)) { switch (data.getInt(INDEX_TYPE)) {
case PGPSignature.DEFAULT_CERTIFICATION: case WrappedSignature.DEFAULT_CERTIFICATION:
mType.setText(R.string.cert_default); mType.setText(R.string.cert_default);
break; break;
case PGPSignature.NO_CERTIFICATION: case WrappedSignature.NO_CERTIFICATION:
mType.setText(R.string.cert_none); mType.setText(R.string.cert_none);
break; break;
case PGPSignature.CASUAL_CERTIFICATION: case WrappedSignature.CASUAL_CERTIFICATION:
mType.setText(R.string.cert_casual); mType.setText(R.string.cert_casual);
break; break;
case PGPSignature.POSITIVE_CERTIFICATION: case WrappedSignature.POSITIVE_CERTIFICATION:
mType.setText(R.string.cert_positive); mType.setText(R.string.cert_positive);
break; break;
case PGPSignature.CERTIFICATION_REVOCATION: { case WrappedSignature.CERTIFICATION_REVOCATION: {
mType.setText(R.string.cert_revoke); mType.setText(R.string.cert_revoke);
if (sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON)) { if (sig.isRevocation()) {
SignatureSubpacket p = sig.getHashedSubPackets().getSubpacket( try {
SignatureSubpacketTags.REVOCATION_REASON); mReason.setText(sig.getRevocationReason());
// For some reason, this is missing in SignatureSubpacketInputStream:146 } catch(PgpGeneralException e) {
if (!(p instanceof RevocationReason)) { mReason.setText(R.string.none);
p = new RevocationReason(false, p.getData());
} }
String reason = ((RevocationReason) p).getRevocationDescription();
mReason.setText(reason);
mRowReason.setVisibility(View.VISIBLE); mRowReason.setVisibility(View.VISIBLE);
} }
break; break;
@ -223,14 +206,11 @@ public class ViewCertActivity extends ActionBarActivity
try { try {
ProviderHelper providerHelper = new ProviderHelper(ViewCertActivity.this); ProviderHelper providerHelper = new ProviderHelper(ViewCertActivity.this);
long signerMasterKeyId = providerHelper.getMasterKeyId( long signerMasterKeyId = providerHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(mCertifierKeyId)) KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mCertifierKeyId)).getMasterKeyId();
); viewIntent.setData(KeyRings.buildGenericKeyRingUri(signerMasterKeyId));
viewIntent.setData(KeyRings.buildGenericKeyRingUri(
Long.toString(signerMasterKeyId))
);
startActivity(viewIntent); startActivity(viewIntent);
} catch (ProviderHelper.NotFoundException e) { } catch (PgpGeneralException e) {
// TODO notify user of this, maybe offer download? // TODO notify user of this, maybe offer download?
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
} }

View File

@ -326,10 +326,10 @@ public class ViewKeyActivity extends ActionBarActivity implements
try { try {
Uri blobUri = Uri blobUri =
KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri); KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
mNfcKeyringBytes = mProviderHelper.getPGPKeyRing( mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData(
blobUri).getEncoded(); blobUri,
} catch (IOException e) { KeychainContract.KeyRingData.KEY_RING_DATA,
Log.e(Constants.TAG, "Error parsing keyring", e); ProviderHelper.FIELD_TYPE_BLOB);
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
} }

View File

@ -33,10 +33,10 @@ import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.TextView; import android.widget.TextView;
import org.spongycastle.openpgp.PGPSignature;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -227,19 +227,19 @@ public class ViewKeyCertsFragment extends LoaderFragment
wSignerKeyId.setText(signerKeyId); wSignerKeyId.setText(signerKeyId);
switch (cursor.getInt(mIndexType)) { switch (cursor.getInt(mIndexType)) {
case PGPSignature.DEFAULT_CERTIFICATION: // 0x10 case WrappedSignature.DEFAULT_CERTIFICATION: // 0x10
wSignStatus.setText(R.string.cert_default); wSignStatus.setText(R.string.cert_default);
break; break;
case PGPSignature.NO_CERTIFICATION: // 0x11 case WrappedSignature.NO_CERTIFICATION: // 0x11
wSignStatus.setText(R.string.cert_none); wSignStatus.setText(R.string.cert_none);
break; break;
case PGPSignature.CASUAL_CERTIFICATION: // 0x12 case WrappedSignature.CASUAL_CERTIFICATION: // 0x12
wSignStatus.setText(R.string.cert_casual); wSignStatus.setText(R.string.cert_casual);
break; break;
case PGPSignature.POSITIVE_CERTIFICATION: // 0x13 case WrappedSignature.POSITIVE_CERTIFICATION: // 0x13
wSignStatus.setText(R.string.cert_positive); wSignStatus.setText(R.string.cert_positive);
break; break;
case PGPSignature.CERTIFICATION_REVOCATION: // 0x30 case WrappedSignature.CERTIFICATION_REVOCATION: // 0x30
wSignStatus.setText(R.string.cert_revoke); wSignStatus.setText(R.string.cert_revoke);
break; break;
} }

View File

@ -32,7 +32,9 @@ import android.widget.ListView;
import com.devspark.appmsg.AppMsg; import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
@ -235,14 +237,16 @@ public class ViewKeyMainFragment extends LoaderFragment implements
return; return;
} }
try { try {
long keyId = new ProviderHelper(getActivity()).extractOrGetMasterKeyId(dataUri); long keyId = new ProviderHelper(getActivity())
.getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
long[] encryptionKeyIds = new long[]{keyId}; long[] encryptionKeyIds = new long[]{keyId};
Intent intent = new Intent(getActivity(), EncryptActivity.class); Intent intent = new Intent(getActivity(), EncryptActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT); intent.setAction(EncryptActivity.ACTION_ENCRYPT);
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
// used instead of startActivity set actionbar based on callingPackage // used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0); startActivityForResult(intent, 0);
} catch (ProviderHelper.NotFoundException e) { } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
} }
} }

View File

@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
@ -190,6 +191,9 @@ public class ViewKeyShareFragment extends LoaderFragment implements
} }
startActivity(Intent.createChooser(sendIntent, title)); startActivity(Intent.createChooser(sendIntent, title));
} }
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "error processing key!", e);
AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
} catch (IOException e) { } catch (IOException e) {
Log.e(Constants.TAG, "error processing key!", e); Log.e(Constants.TAG, "error processing key!", e);
AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show(); AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();

View File

@ -32,6 +32,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Highlighter; import org.sufficientlysecure.keychain.util.Highlighter;
@ -83,7 +84,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
return mData; return mData;
} }
public ArrayList<ImportKeysListEntry> getSelectedData() { public ArrayList<ImportKeysListEntry> getSelectedEntries() {
ArrayList<ImportKeysListEntry> selectedData = new ArrayList<ImportKeysListEntry>(); ArrayList<ImportKeysListEntry> selectedData = new ArrayList<ImportKeysListEntry>();
for (ImportKeysListEntry entry : mData) { for (ImportKeysListEntry entry : mData) {
if (entry.isSelected()) { if (entry.isSelected()) {

View File

@ -19,19 +19,19 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context; import android.content.Context;
import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.util.LongSparseArray;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream; import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
public class ImportKeysListLoader public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
@ -56,6 +56,7 @@ public class ImportKeysListLoader
final InputData mInputData; final InputData mInputData;
ArrayList<ImportKeysListEntry> mData = new ArrayList<ImportKeysListEntry>(); ArrayList<ImportKeysListEntry> mData = new ArrayList<ImportKeysListEntry>();
LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<ParcelableKeyRing>();
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper; AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
public ImportKeysListLoader(Context context, InputData inputData) { public ImportKeysListLoader(Context context, InputData inputData) {
@ -107,6 +108,10 @@ public class ImportKeysListLoader
super.deliverResult(data); super.deliverResult(data);
} }
public LongSparseArray<ParcelableKeyRing> getParcelableRings() {
return mParcelableRings;
}
/** /**
* Reads all PGPKeyRing objects from input * Reads all PGPKeyRing objects from input
* *
@ -116,7 +121,6 @@ public class ImportKeysListLoader
private void generateListOfKeyrings(InputData inputData) { private void generateListOfKeyrings(InputData inputData) {
boolean isEmpty = true; boolean isEmpty = true;
int nonPgpCounter = 0;
PositionAwareInputStream progressIn = new PositionAwareInputStream( PositionAwareInputStream progressIn = new PositionAwareInputStream(
inputData.getInputStream()); inputData.getInputStream());
@ -129,28 +133,18 @@ public class ImportKeysListLoader
// read all available blocks... (asc files can contain many blocks with BEGIN END) // read all available blocks... (asc files can contain many blocks with BEGIN END)
while (bufferedInput.available() > 0) { while (bufferedInput.available() > 0) {
isEmpty = false; // todo deal with non-keyring objects?
InputStream in = PGPUtil.getDecoderStream(bufferedInput); List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in); for(UncachedKeyRing key : rings) {
ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key);
// go through all objects in this block mData.add(item);
Object obj; mParcelableRings.put(key.getMasterKeyId(), new ParcelableKeyRing(key.getEncoded()));
while ((obj = objectFactory.nextObject()) != null) { isEmpty = false;
Log.d(Constants.TAG, "Found class: " + obj.getClass());
if (obj instanceof PGPKeyRing) {
PGPKeyRing newKeyring = (PGPKeyRing) obj;
addToData(newKeyring);
} else {
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
nonPgpCounter++;
}
} }
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(Constants.TAG, "Exception on parsing key file!", e); Log.e(Constants.TAG, "Exception on parsing key file!", e);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e); mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
nonPgpCounter = 0;
} }
if (isEmpty) { if (isEmpty) {
@ -158,16 +152,6 @@ public class ImportKeysListLoader
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
(mData, new FileHasNoContent()); (mData, new FileHasNoContent());
} }
if (nonPgpCounter > 0) {
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
(mData, new NonPgpPart(nonPgpCounter));
}
}
private void addToData(PGPKeyRing keyring) {
ImportKeysListEntry item = new ImportKeysListEntry(getContext(), keyring);
mData.add(item);
} }
} }

View File

@ -41,17 +41,12 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast; import android.widget.Toast;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -106,8 +101,12 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
long secretKeyId) throws PgpGeneralException { long secretKeyId) throws PgpGeneralException {
// check if secret key has a passphrase // check if secret key has a passphrase
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) { if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
if (!PassphraseCacheService.hasPassphrase(context, secretKeyId)) { try {
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!"); if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
}
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException("Error: Key not found!", e);
} }
} }
@ -139,18 +138,24 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
alert.setTitle(R.string.title_authentication); alert.setTitle(R.string.title_authentication);
final PGPSecretKey secretKey; final WrappedSecretKeyRing secretRing;
final String userId; String userId;
if (secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none) { if (secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none) {
secretKey = null;
alert.setMessage(R.string.passphrase_for_symmetric_encryption); alert.setMessage(R.string.passphrase_for_symmetric_encryption);
secretRing = null;
} else { } else {
try { try {
ProviderHelper helper = new ProviderHelper(activity); ProviderHelper helper = new ProviderHelper(activity);
secretKey = helper.getPGPSecretKeyRing(secretKeyId).getSecretKey(); secretRing = helper.getWrappedSecretKeyRing(secretKeyId);
userId = (String) helper.getUnifiedData(secretKeyId, // yes the inner try/catch block is necessary, otherwise the final variable
KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING); // above can't be statically verified to have been set in all cases because
// the catch clause doesn't return.
try {
userId = secretRing.getPrimaryUserId();
} catch (PgpGeneralException e) {
userId = null;
}
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
alert.setTitle(R.string.title_key_not_found); alert.setTitle(R.string.title_key_not_found);
alert.setMessage(getString(R.string.key_not_found, secretKeyId)); alert.setMessage(getString(R.string.key_not_found, secretKeyId));
@ -179,76 +184,59 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
@Override @Override
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
dismiss(); dismiss();
long curKeyIndex = 1;
boolean keyOK = true;
String passphrase = mPassphraseEditText.getText().toString(); String passphrase = mPassphraseEditText.getText().toString();
long keyId;
PGPSecretKey clickSecretKey = secretKey;
if (clickSecretKey != null) { // Early breakout if we are dealing with a symmetric key
while (keyOK) { if (secretRing == null) {
if (clickSecretKey != null) { // check again for loop PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric, passphrase);
try { // also return passphrase back to activity
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() Bundle data = new Bundle();
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
passphrase.toCharArray()); sendMessageToHandler(MESSAGE_OKAY, data);
PGPPrivateKey testKey = clickSecretKey return;
.extractPrivateKey(keyDecryptor);
if (testKey == null) {
if (!clickSecretKey.isMasterKey()) {
Toast.makeText(activity,
R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return;
} else {
try {
clickSecretKey = PgpKeyHelper.getKeyNum(new ProviderHelper(activity)
.getPGPSecretKeyRingWithKeyId(secretKeyId),
curKeyIndex
);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
curKeyIndex++; // does post-increment work like C?
continue;
}
} else {
keyOK = false;
}
} catch (PGPException e) {
Toast.makeText(activity, R.string.wrong_passphrase,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return;
}
} else {
Toast.makeText(activity, R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return; // ran out of keys to try
}
}
keyId = secretKey.getKeyID();
} else {
keyId = Constants.key.symmetric;
} }
WrappedSecretKey unlockedSecretKey = null;
for(WrappedSecretKey clickSecretKey : secretRing.iterator()) {
try {
boolean unlocked = clickSecretKey.unlock(passphrase);
if (unlocked) {
unlockedSecretKey = clickSecretKey;
break;
}
} catch (PgpGeneralException e) {
Toast.makeText(activity, R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return; // ran out of keys to try
}
}
// Means we got an exception every time
if (unlockedSecretKey == null) {
Toast.makeText(activity, R.string.wrong_passphrase,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return;
}
long masterKeyId = secretRing.getMasterKeyId();
// cache the new passphrase // cache the new passphrase
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
PassphraseCacheService.addCachedPassphrase(activity, keyId, passphrase); PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase);
if (!keyOK && clickSecretKey.getKeyID() != keyId) { if (unlockedSecretKey.getKeyId() != masterKeyId) {
PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(), PassphraseCacheService.addCachedPassphrase(
passphrase); activity, unlockedSecretKey.getKeyId(), passphrase);
} }
// also return passphrase back to activity // also return passphrase back to activity
Bundle data = new Bundle(); Bundle data = new Bundle();
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase); data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
sendMessageToHandler(MESSAGE_OKAY, data); sendMessageToHandler(MESSAGE_OKAY, data);
} }
}); });

View File

@ -38,22 +38,18 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton; import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Choice; import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.Vector;
public class KeyEditor extends LinearLayout implements Editor, OnClickListener { public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private PGPSecretKey mKey; private UncachedSecretKey mKey;
private EditorListener mEditorListener = null; private EditorListener mEditorListener = null;
@ -208,7 +204,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
} }
} }
public void setValue(PGPSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) { public void setValue(UncachedSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
mKey = key; mKey = key;
mIsMasterKey = isMasterKey; mIsMasterKey = isMasterKey;
@ -216,13 +212,12 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mDeleteButton.setVisibility(View.INVISIBLE); mDeleteButton.setVisibility(View.INVISIBLE);
} }
mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(getContext(), key)); mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(getContext(), key.getAlgorithm()));
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyID()); String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyId());
mKeyId.setText(keyIdStr); mKeyId.setText(keyIdStr);
Vector<Choice> choices = new Vector<Choice>(); boolean isElGamalKey = (key.isElGamalEncrypt());
boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT); boolean isDSAKey = (key.isDSA());
boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA);
if (isElGamalKey) { if (isElGamalKey) {
mChkSign.setVisibility(View.INVISIBLE); mChkSign.setVisibility(View.INVISIBLE);
TableLayout table = (TableLayout) findViewById(R.id.table_keylayout); TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
@ -248,38 +243,45 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mIsNewKey = isNewKey; mIsNewKey = isNewKey;
if (isNewKey) { if (isNewKey) {
mUsage = usage; mUsage = usage;
mChkCertify.setChecked((usage & KeyFlags.CERTIFY_OTHER) == KeyFlags.CERTIFY_OTHER); mChkCertify.setChecked(
mChkSign.setChecked((usage & KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA); (usage & UncachedSecretKey.CERTIFY_OTHER) == UncachedSecretKey.CERTIFY_OTHER);
mChkEncrypt.setChecked(((usage & KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS) || mChkSign.setChecked(
((usage & KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE)); (usage & UncachedSecretKey.SIGN_DATA) == UncachedSecretKey.SIGN_DATA);
mChkAuthenticate.setChecked((usage & KeyFlags.AUTHENTICATION) == KeyFlags.AUTHENTICATION); mChkEncrypt.setChecked(
((usage & UncachedSecretKey.ENCRYPT_COMMS) == UncachedSecretKey.ENCRYPT_COMMS) ||
((usage & UncachedSecretKey.ENCRYPT_STORAGE) == UncachedSecretKey.ENCRYPT_STORAGE));
mChkAuthenticate.setChecked(
(usage & UncachedSecretKey.AUTHENTICATION) == UncachedSecretKey.AUTHENTICATION);
} else { } else {
mUsage = PgpKeyHelper.getKeyUsage(key); mUsage = key.getKeyUsage();
mOriginalUsage = mUsage; mOriginalUsage = mUsage;
if (key.isMasterKey()) { if (key.isMasterKey()) {
mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key)); mChkCertify.setChecked(key.canCertify());
} }
mChkSign.setChecked(PgpKeyHelper.isSigningKey(key)); mChkSign.setChecked(key.canSign());
mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key)); mChkEncrypt.setChecked(key.canEncrypt());
mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key)); mChkAuthenticate.setChecked(key.canAuthenticate());
} }
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); {
cal.setTime(PgpKeyHelper.getCreationDate(key)); Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
setCreatedDate(cal); cal.setTime(key.getCreationTime());
cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); setCreatedDate(cal);
Date expiryDate = PgpKeyHelper.getExpiryDate(key); }
Date expiryDate = key.getExpiryTime();
if (expiryDate == null) { if (expiryDate == null) {
setExpiryDate(null); setExpiryDate(null);
} else { } else {
cal.setTime(PgpKeyHelper.getExpiryDate(key)); Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTime(expiryDate);
setExpiryDate(cal); setExpiryDate(cal);
mOriginalExpiryDate = cal; mOriginalExpiryDate = cal;
} }
} }
public PGPSecretKey getValue() { public UncachedSecretKey getValue() {
return mKey; return mKey;
} }
@ -320,16 +322,16 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
} }
public int getUsage() { public int getUsage() {
mUsage = (mUsage & ~KeyFlags.CERTIFY_OTHER) | mUsage = (mUsage & ~UncachedSecretKey.CERTIFY_OTHER) |
(mChkCertify.isChecked() ? KeyFlags.CERTIFY_OTHER : 0); (mChkCertify.isChecked() ? UncachedSecretKey.CERTIFY_OTHER : 0);
mUsage = (mUsage & ~KeyFlags.SIGN_DATA) | mUsage = (mUsage & ~UncachedSecretKey.SIGN_DATA) |
(mChkSign.isChecked() ? KeyFlags.SIGN_DATA : 0); (mChkSign.isChecked() ? UncachedSecretKey.SIGN_DATA : 0);
mUsage = (mUsage & ~KeyFlags.ENCRYPT_COMMS) | mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_COMMS) |
(mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_COMMS : 0); (mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_COMMS : 0);
mUsage = (mUsage & ~KeyFlags.ENCRYPT_STORAGE) | mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_STORAGE) |
(mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_STORAGE : 0); (mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_STORAGE : 0);
mUsage = (mUsage & ~KeyFlags.AUTHENTICATION) | mUsage = (mUsage & ~UncachedSecretKey.AUTHENTICATION) |
(mChkAuthenticate.isChecked() ? KeyFlags.AUTHENTICATION : 0); (mChkAuthenticate.isChecked() ? UncachedSecretKey.AUTHENTICATION : 0);
return mUsage; return mUsage;
} }

View File

@ -35,10 +35,9 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton; import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.openpgp.PGPKeyFlags;
import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@ -63,7 +62,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
private int mNewKeySize; private int mNewKeySize;
private boolean mOldItemDeleted = false; private boolean mOldItemDeleted = false;
private ArrayList<String> mDeletedIDs = new ArrayList<String>(); private ArrayList<String> mDeletedIDs = new ArrayList<String>();
private ArrayList<PGPSecretKey> mDeletedKeys = new ArrayList<PGPSecretKey>(); private ArrayList<UncachedSecretKey> mDeletedKeys = new ArrayList<UncachedSecretKey>();
private boolean mCanBeEdited = true; private boolean mCanBeEdited = true;
private ActionBarActivity mActivity; private ActionBarActivity mActivity;
@ -227,7 +226,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
return mDeletedIDs; return mDeletedIDs;
} }
public ArrayList<PGPSecretKey> getDeletedKeys() { public ArrayList<UncachedSecretKey> getDeletedKeys() {
return mDeletedKeys; return mDeletedKeys;
} }
@ -325,7 +324,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
this.updateEditorsVisible(); this.updateEditorsVisible();
} }
public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages, boolean newKeys) { public void setKeys(Vector<UncachedSecretKey> list, Vector<Integer> usages, boolean newKeys) {
if (mType != TYPE_KEY) { if (mType != TYPE_KEY) {
return; return;
} }
@ -358,9 +357,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
String passphrase; String passphrase;
if (mEditors.getChildCount() > 0) { if (mEditors.getChildCount() > 0) {
PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue(); UncachedSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
passphrase = PassphraseCacheService passphrase = PassphraseCacheService
.getCachedPassphrase(mActivity, masterKey.getKeyID()); .getCachedPassphrase(mActivity, masterKey.getKeyId());
isMasterKey = false; isMasterKey = false;
} else { } else {
passphrase = ""; passphrase = "";
@ -395,7 +394,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
// get new key from data bundle returned from service // get new key from data bundle returned from service
Bundle data = message.getData(); Bundle data = message.getData();
PGPSecretKey newKey = (PGPSecretKey) PgpConversionHelper UncachedSecretKey newKey = PgpConversionHelper
.BytesToPGPSecretKey(data .BytesToPGPSecretKey(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY)); .getByteArray(KeychainIntentService.RESULT_NEW_KEY));
addGeneratedKeyToView(newKey); addGeneratedKeyToView(newKey);
@ -413,14 +412,14 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mActivity.startService(intent); mActivity.startService(intent);
} }
private void addGeneratedKeyToView(PGPSecretKey newKey) { private void addGeneratedKeyToView(UncachedSecretKey newKey) {
// add view with new key // add view with new key
KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
mEditors, false); mEditors, false);
view.setEditorListener(SectionView.this); view.setEditorListener(SectionView.this);
int usage = 0; int usage = 0;
if (mEditors.getChildCount() == 0) { if (mEditors.getChildCount() == 0) {
usage = PGPKeyFlags.CAN_CERTIFY; usage = UncachedSecretKey.CERTIFY_OTHER;
} }
view.setValue(newKey, newKey.isMasterKey(), usage, true); view.setValue(newKey, newKey.isMasterKey(), usage, true);
mEditors.addView(view); mEditors.addView(view);

View File

@ -517,6 +517,7 @@
<string name="title_view_cert">View Certificate Details</string> <string name="title_view_cert">View Certificate Details</string>
<string name="unknown_algorithm">unknown</string> <string name="unknown_algorithm">unknown</string>
<string name="can_sign_not">cannot sign</string> <string name="can_sign_not">cannot sign</string>
<string name="error_encoding">Encoding error</string>
<string name="error_no_encrypt_subkey">No encryption subkey available!</string> <string name="error_no_encrypt_subkey">No encryption subkey available!</string>
</resources> </resources>

View File

@ -1,46 +0,0 @@
package org.sufficientlysecure.keychain;
import org.junit.Before;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.sufficientlysecure.keychain.pgp.*;
import org.spongycastle.openpgp.*;
@RunWith(RobolectricGradleTestRunner.class)
public class PgpKeyOperationTest {
PGPSecretKey key;
@Before
public void setUp() throws Exception {
/* Input */
int algorithm = Id.choice.algorithm.dsa;
String passphrase = "swag";
int keysize = 2048;
boolean masterKey = true;
/* Operation */
PgpKeyOperation keyOperations = new PgpKeyOperation(null);
key = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);
System.err.println("initialized, test key: " + PgpKeyHelper.convertKeyIdToHex(key.getKeyID()));
}
@After
public void tearDown() {
}
@Test
public void createTest() {
}
@Test
public void certifyKey() {
System.err.println("swag");
}
}

View File

@ -1,23 +0,0 @@
package org.sufficientlysecure.keychain;
import org.junit.runners.model.InitializationError;
import org.robolectric.AndroidManifest;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.res.Fs;
import org.robolectric.res.FsFile;
import org.sufficientlysecure.keychain.KeychainApplication;
public class RobolectricGradleTestRunner extends RobolectricTestRunner {
public RobolectricGradleTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
@Override protected AndroidManifest getAppManifest(Config config) {
String myAppPath = KeychainApplication.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String manifestPath = myAppPath + "../../../src/main/AndroidManifest.xml";
return createAppManifest(Fs.fileFromPath(manifestPath));
}
}

View File

@ -5,6 +5,7 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:0.10.0' classpath 'com.android.tools.build:gradle:0.10.0'
classpath 'org.robolectric.gradle:gradle-android-test-plugin:0.9.+'
} }
} }

2
extern/spongycastle vendored

@ -1 +1 @@
Subproject commit eb2c35bd0602d05a65c10c86b9c9834ebd1a81c6 Subproject commit 2c47e5fca2a820a4fd584066871bed993f1c3919